This discusses color printing in a Macintosh application.
Whereas the original eight-color model of QuickDraw was sufficient for printing in color on the ImageWriter II, the introduction of Color QuickDraw has created the need for more sophisticated printing methods.
The first section describes using the eight-color QuickDraw model with the ImageWriter II and ImageWriter LQ drivers. Since the current Print Manager does not support Color GrafPorts, the eight-color model is the only method available for the ImageWriters.
The next section describes a technique that can be used for printing halftone images using PostScript (when it is available). Also described is a device independent technique for sending the PostScript data. This technique can be used on any LaserWriter driver 3.0 or later. It will work with all LaserWriters except the the LaserWriter IISC.
It is very likely that better color support will be added to the Print Manager in the future. Until then, these are the best methods available.
Part 1, ImageWriters
The ImageWriter drivers are capable of generating each of the eight standard colors defined in QuickDraw by the following constants:
whiteColor
blackColor
redColor
greenColor
blueColor
cyanColor
magentaColor
yellowColor
To generate color all you need to do is set the foreground and background colors before you begin drawing (initially they are set to blackColor foreground and whiteColor background). To do this you call the QuickDraw routines ForeColor and BackColor as described in Inside Macintosh. If you are using QuickDraw pictures, make sure you set the foreground and background colors before you call ClosePicture so that they are recorded in the picture. Setting the colors before calling DrawPicture doesn’t work.
The drivers also recognize two of the transfer modes: srcCopy and srcOr. The effect of the other transfer modes is not well defined and has not been tested. It may be best to stay away from them.
Caveats
When printing a large area of more than one color you will encounter a problem with the ribbon. When you print a large area of one color, the printer’s pins pick up the color from the back of the ribbon. When another large area of color is printed, the pins deposit the previous color onto the back of the ribbon. Eventually the first color will come through to the front of the ribbon, contaminating the second color. You can get the same kind of effect if you set, for example, a foreground color of yellow and a background color of blue. The ribbon will pick up the blue as it tries to print yellow on top of it. This problem is partially alleviated in the 2.3 version of the ImageWriter driver by using a different printing technique.
The ribbon goes through the printer rather quickly when printing large areas. When the ribbon comes through the second time the colors don’t look too great.
Part 2, LaserWriters
Using the PostScript ‘image’ Operator to Print Halftones
About ‘image’
The PostScript image operator is used to send Bitmaps or Pixmaps to the LaserWriter. The image operator can handle depths from 1 to 8 bits per pixel. Our current LaserWriters can only image about twenty shades of gray, but the printed page will look like there’s more. Being that the image operator is still a PostScript operator, it expects its data in the form of hexidecimal bytes. The bytes are represented by two ASCII characters(0-9,A-F). The image operator takes these parameters:
width height depth matrix image-data
The first three are the width, height, and depth of the image, and the matrix is the transformation matrix to be applied to the current matrix. See the PostScript Language Reference Manual for more information. The image data is where the actual hex data should go. Instead of inserting the data between the first parameters and the image operator itself, it is better to use a small, PostScript procedure to read the data starting from right after the image operator. For example:
640 480 8 [640 0 0 480 0 0]
{currentfile picstr readhexstring pop}
image
FF 00 FF 00 FF 00 FF 00 ...
In the above example, the width of the image is 640, the height is 480, and the depth is 8. The matrix (enclosed in brackets) is setup to draw the image starting at QuickDraw’s 0,0 (top left of page), and with no scaling. The PostScript code (enclosed in braces) is not executed. Instead, it is passed to the image operator, and the image operator will call it repeatedly until it has enough data to draw the image. In this case, it will be expecting 640*480 bytes. When the image operator calls the procedure, it does the following:
1. Pushes the current file which in this case is the stream of data coming to the LaserWriter over AppleTalk. This is the first parameter to readhexstring.
2. Next picstr is pushed. picstr is a string variable defined to hold one row of hex data. The PostScript to create the picstr is:
/picstr 640 def
3. Now readhexstring is called to fill picstr with data from the current file. It begins reading bytes which are the characters following the image operator.
4. Since readhexstring leaves both the string we want, and a boolean that we don’t want on the stack, we do one pop to kill of the boolean. Now the string is left behind for the image operator to use.
So using the above PostScript code you can easily print an image. Just fill in the width height and depth, and send the hex data immediately following the PostScript code.
Setting Up for ‘image’
Most of the users of this technique are going to want to print a Color QuickDraw PixMap. Although the image command does a lot of the work for you, there are still a couple of tricks that are recommended for performance.
Assume the Maximum Depth
Since the current version of the image operator has a maximum depth of 8 bits/pixel, it is wise to convert the source image to the same depth before imaging. This can be done very simply by using an offscreen GrafPort that is set to 8 bits/pixel, and then using CopyBits to do the depth conversion for you. This will do a nice job of converting lower resolution images to 8 bits/pixel.
Build a Color Table
An 8 bit deep image can only use 256 colors. Since the image that you are starting with is probably color, and the image you get will be grayscale, you need to convert the colors in the source color table into PostScript grayscale values. This is actually easy to do using the Color Manager. First create a table that can hold 512 bytes. This is 2 bytes for each color value from 0 to 255. Since PostScript wants the values in ASCII, you need two characters for each pixel. Now loop through the colors in the color table. Call Index2Color to get the real RGB color for that index, and then call RGB2HSL to convert the RGB color into a luminance value. This value will be expressed as a SmallFract which can then be scaled into a value from 0 to 255. This value should then be converted to ASCII, and stored at the appropriate location in the table. When you are done, you should be able to use a pixel value as an index into your table of PostScript color values. For each pixel in the image, send two characters to the LaserWriter.
Sending the Data
Once you have set up the color table, all that left to do is to loop through all of the pixels, and send their PostScript representation to the LaserWriter. There are a couple of ways to do this. First is to use the low-level Print Manager interface and stream the PostScript using the stdBuf PrCtlCall. Although this seems like it would be the fastest way, the latest version of the LaserWriter driver (5.0) converts all low-level calls to their high level equivalent before executing them. Because of this, the low-level interface is no longer faster than the high level. In an FKEY I have written, I use the high-level Print Manager interface, and send the data via the PostScriptHandle PicComment. This way, I can buffer a large amount of data, before actually sending it. Using this technique, I have been able to image a Mac II screen in about 5 minutes on a LaserWriter Plus, and about 1.5 minutes on a LaserWriter II NTX.
Further Reference:
• QuickDraw
• The Printing Manager
• PostScript Language Reference Manual, Adobe Systems
PR 2 - Device-Independent Printing
Printing
Revised by: March 1988
Written by: Ginger Jernigan May 1987
The Printing Manager was designed to give Macintosh applications a device- independent method of printing, but we have provided device-dependent information, such as the contents of the print record. Due to the large number of printer-type drivers becoming available (even for non-printer devices) device independence is more necessary than ever. What this means to you, as a developer, is that we will no longer be providing (or supporting) information regarding the internal structure of the print record.
We realize that there are situations where the application may know the best method for printing a particular document and may want to bypass our dialogs. Unfortunately, using your own dialogs or not using the dialogs at all, requires setting the necessary fields in the print record yourself. There are a number of problems:
• Many of the fields in the print record are undocumented, and, as we change the internal architecture of the Printing Manager to accommodate new devices, those undocumented fields are likely to change.
• Each driver uses the private, and many of the public, fields in the print record differently. The implications are that you would need intimate knowledge of how each field is used by each available driver, and you would have to set the fields in the record differently depending on the driver chosen. As the number of available printer-type drivers increases, this can become a cumbersome task.
Summary
To be compatible with future printer-like devices, it is essential that your application print in a device-independent manner. Avoid testing undocumented fields, setting fields in the print record directly and bypassing the existing print dialogs. Use the Printing Manager dialogs, PrintDefault and PrValidate to set up the print record for you.
Further Reference:
• The Printing Manager
PR 3 - Document Names and the Printing Manager
Printing
Revised by: March 1988
Written by: Bryan Stearns July 1987
Our compatibility testing for LaserShare (Apple’s LaserWriter spooler) has turned up a number of applications that do not provide the Printing Manager with a document name; although this feature is not required, it is nice for users that share printers.
Some printers (usually those that are shared between many users, like the LaserWriter) can provide the names of the users who are printing and the documents that are being printed to others interested in using the printer.
If the chosen printer uses a document name, the Printing Manager gets the name from the frontmost window’s title. If there is no front window, or if the window’s title is empty, the Printing Manager defaults to “unknown.”
This method was chosen because it works most transparently to applications; however, it won’t work if your application doesn’t display windows when printing (for instance, many applications that use windows for their documents do not open their documents when printing in response to a Finder “Print” command).
As a general solution to this problem, you can put up a window containing a message like “Press –. to cancel printing”, and give it the document’s title. If the window is one that doesn’t have a title bar (like dBoxProc), this title will not be displayed. MacApp takes this approach. If for some reason you don’t want to put up a visible window, you can create a tiny window and hide it behind the menu bar: for instance, global coordinates of (1,1,2,2). Make sure you use a plainDBox, so that no title will be drawn (otherwise, in the unlikely case that a user is using a Macintosh II with two stacked screens, main screen on the bottom, the title might be visible on the upper screen).
Since the Printing Manager checks the name at PrValidate time, call PrValidate after PrCloseDoc and before the next PrOpenDoc, if you want unique names.
A number of applications set the document name in the print record directly. You should not do this because a) not all printers support this field, and b) none are guaranteed to support it in the future. (Apple does not guarantee that internal fields of the Printing Manager’s data structures will remain the same; the Printing Manager is targeted for substantial internal change!)
This Technical Note discusses techniques for optimizing code for printing on the LaserWriter.
Changes since March 1988: Updated the “Printable Paper Area” and “Memory Considerations” sections as well as the printer IDs, moved the error messages from the end of the Note to Technical Note #161, A Printing Loop That Cares…, and removed the “Spool-A-Page/Print-A-Page” section because Technical Note #125, Effect of Spool-A-Page/Print-A-Page on Shared Printers, already thoroughly covers this topic.
Introduction
Although the Printing Manager was originally designed to allow application code to be printer independent, there are some things about the LaserWriter that, in some cases, have to be addressed in a printer dependent way. This Note describes what the LaserWriter can and cannot do, memory considerations, speed considerations, as well as other things you need to watch out for if you want to make your printing more efficient on the LaserWriter.
How To Determine The Currently Selected Printer
With the addition of new picture comments and the PrGeneral procedure, an application should never need to know the type of device to which it is connected. However, some developers feel their application should be able to take advantage of all of the features provided by a particular device, not just those provided by the Printing Manager, and in doing so, these developers produce device-dependent applications, which can produce unpredictable results third-party and new Apple printing devices. For this reason, Apple strongly recommends that you use only the features provided by the Printing Manager, and do not try to use unsupported device features.
Even though there is no supported method for determining a device’s type, there is one method described in the original Inside Macintosh that still works for ImageWriter and LaserWriter printer drivers. This method is not supported, meaning that at some point in the future it will no longer work. If you use this method in your application, it is up to you to weigh the value of the feature against the compatibility risk. The following method works for all ImageWriter, ImageWriter II, and LaserWriter (original, Plus, IInt, IIntx) drivers. Since all new devices released from Apple and third-party developers have their own unique ID, it is up to you to decide what to do with an ID that your application does not recognize.
If you are using the high-level Printing Manager interface, first call PrValidate to make sure you have the correct print record. Look at the high byte of the wdev word in the TPrStl subrecord of the print record. Note that if you have your own driver and want to have your own number, please let DTS know, and DTS can register it.
Following is the current list of printer IDs:
Printer wDev
ImageWriter I, ImageWriter II 1
LaserWriter, LaserWriter Plus, Laser-
Writer IInt, LaserWriter IIntx, and
Personal LaserWriter nt 3
LaserWriter IIsc, Personal LaserWriter sc 4
ImageWriter LQ 5
If you are using the low-level Printing Manager interface, there is no dependable way of getting the wDev information. You should not attempt to determine the device ID when using the low-level Printing Manager interface.
Using QuickDraw With the LaserWriter
When you print to the LaserWriter, all of the QuickDraw calls you make are translated (via QuickDraw bottlenecks) into PostScript®, which is in the LaserWriter ROM. Most of the operations available in QuickDraw are available in PostScript, with a few exceptions. The LaserWriter driver does not support the following:
• XOR and NotXOR transfer modes.
• The grafverb invert.
• _SetOrigin calls within PrOpenPage and PrClosePage calls. Use _OffsetRect instead. (This is fixed in version 3.0 and later of the driver.)
• Regions are ignored. You can simulate regions using polygons or bitmaps. Refer to Technical Note #41, Drawing Into An Off-Screen Bitmap, for how to create off-screen bitmaps.
• Clip regions should be limited to rectangles.
• There is a small difference in character widths between screen fonts and printer fonts. Only the end points of text strings are the same.
What You See Is Not Always What You Get
Unfortunately, what you see on the screen is not always what you get. If you are using standard graphic objects, like rectangles, circles, etc., the object is the same size on the LaserWriter as it is on the screen. There are, however, two types of objects where this is not the case: text and bitmaps.
The earlier noted difference between the widths of characters on the screen and the widths of characters on the printer is due to the difference in resolution. However, to maintain the integrity of line breaks, the driver changes the word and character spacing to maintain the end points of the lines as specified. What this all means is that you cannot count on the positions or the widths of printed characters being exactly the same as they are on the screen. This is why in the original MacDraw®, for example, if one carefully places text and a rectangle and prints it, the text sometimes extends beyond the bounds of the rectangle on the printed page. If an application does its own line layout (i.e., positions the words on the line itself), then it may want to disable the LaserWriter’s line layout routines. To disable these routines, use the LineLayoutOff picture comment described in the LaserWriter Reference Manual and Technical Note #91, Optimizing for the LaserWriter—Picture Comments.
The sole exception to this rule is if an application is running on 128K ROMs or later. The 128K ROM Font Manager supports the specification of fractional pixel widths for screen fonts, increasing the screen to printer accuracy. This fractional width feature is disabled by default. To enable it, an application can use _SetFractEnable, after calling _InitFonts.
Applications can use picture comments to left-, right-, or center-justify text. Only the left, right, or center end points are accurate. If the text is fully justified, both end points are accurate. Technical Note #91, Optimizing for the LaserWriter—Picture Comments, discusses these picture comments.
Memory Considerations
To print to the LaserWriter, you need to make sure that you have enough memory available to load the driver’s code. The best way to do this is to have all the code you need for printing in a separate segment and unload everything else. When you print to the LaserWriter you are only able to print in Draft mode. You are not able to spool (as the ImageWriter does in the standard or high-quality settings), and your print code, data, and the driver code have to be resident in memory.
In terms of memory requirements, there is not any magic number that always works with all printer drivers (including third-party printer drivers) that are available for the Macintosh. To make sure there is enough memory available during print time, you should make your printing code a separate segment and swap out all unwanted code and data before you call _PrOpen.
Printable Paper Area
On the LaserWriter there is a 0.45-inch border that surrounds the printable area of the paper (this is assuming an 8.5” x 11” paper). If you select the “Larger Print Area” option in the Page Setup dialog box, the border changes to 0.25 of an inch. This printable area is different than the available print area of the ImageWriter. An application cannot print a larger area because of the memory PostScript needs to image a page. PostScript takes the amount of memory available in the printer and centers it on the paper, and there is not enough RAM in the LaserWriter to image an entire sheet of paper.
Page Sizes
Many developers have expressed a desire to support page sizes other than those provided by the Apple printer drivers. Even though some devices can physically support other page sizes, there is no way for an application to tell the driver to use this size. With the ImageWriter driver, it is possible to modify certain fields in the print record and expand the printable area of the page. However, each of the Apple drivers implements the page sizes in a different way. No one method works for all drivers. Because of this difference, it is strongly recommended that applications do not attempt to change the page sizes provided in the “Style” dialog box. If your application currently supports page sizes other than those provided by the printer driver, you are taking a serious compatibility risk with future Apple and third-party printer drivers.
Speed Considerations
Although the LaserWriter is relatively fast, there are some techniques an application can use to ensure its maximum performance.
• Try to avoid using the QuickDraw Erase calls (e.g., _EraseRect, _EraseOval, etc.). It takes a lot of time to handle the erase function because every bit (90,000 bits per square inch) has to be cleared. Erasing is unnecessary because the paper does not need to be erased the way the screen does.
• Printing patterns takes time, since the bitmap for the pattern has to be built. The patterns black, white, and all the gray patterns have been optimized to use the PostScript gray scales. If you use a different pattern it works, but it just takes longer than usual. In addition, the patterns in driver version 3.0 are rotated; they are not rotated in version 1.0.
• Try to avoid frequently changing fonts. PostScript has to build each character it needs either by using the drawing commands for the built-in LaserWriter fonts or by resizing bitmaps downloaded from screen fonts on the Macintosh. As each character is built, it is cached (if there’s room), so if that character is needed again PostScript gets if from the cache. When the font changes, the characters have to be built from scratch in the new font, which takes time. If the font is not in the LaserWriter, it takes time to download it from the Macintosh. If the user has the option of choosing fonts, you have no control over this variable; however, if you control which fonts to use, keep this in mind.
• Avoid using _TextBox. It makes calls to _EraseRect, which slows the printer, for every line of text it draws. You might want to use a different method of displaying text (e.g., _DrawString or _DrawText) or write your own version of _TextBox. If an application is currently calling _TextBox, changing to another method of displaying text can improve speed on the order of five to one.
• Because of the way rectangle intersections are determined, if your clip region falls outside of the rPage rectangle, you slow down the printer substantially. By making sure your clip region is entirely within the rPage rectangle, you can get a speed improvement of approximately four to one.
• Do not use spool-a-page/print-a-page as some applications do when printing on the ImageWriter. It slows things down considerably because of all of the preparation that has to be done when a job is initiated. Refer to Technical Note #125, Effect of Spool-A-Page/Print-A-Page on Shared Printers, for more information.
• Using _DrawChar to place every character to print can take a lot of time. One reason, of course, is because it has to go through the bottlenecks for every character that is drawn. The other is that the printer driver does its best to do line layout, making the character spacing just right. If you are trying to position characters and the driver is trying to position characters too, there is conflict, and printing takes much longer than necessary. In version 3.0 of the driver, there are picture comments that turn off the line layout optimization, alleviating some of the problem. Refer to Technical Note #91, Optimizing for the LaserWriter—Picture Comments, for more information.
Clipping Within Text Strings
When clipping characters out of a string, make sure that the clipping rectangle or region is greater than the bounding box of the text you want to clip. The reason is that if you clip part of a character (e.g., a descender), the clipped character has to be rebuilt, which takes time. In addition, because of the difference between screen fonts and printer fonts, chances are that you cannot accurately clip the right characters unless you are running on the 128K ROMs and have fractional pixel widths enabled.
When to Validate the Print Record
To validate the print record, call PrValidate. You need validation to check to see if all of the fields are accurate according to the current printer selected and the current version of the driver. You should call PrValidate when you have allocated a new print record or whenever you need to access information from the print record (i.e., when you get rPage). The routines PrStlDialog and PrJobDialog call PrValidate when they are called, so you do not have to worry about it if you use these calls.
Empty QuickDraw Objects
QuickDraw objects that are empty (i.e., they have no pixels in them) and are filled but not framed, do not print on the ImageWriter and do not show up on the screen; however, on the LaserWriter they are real objects and do print.
Further Reference:
• Inside Macintosh, Volume I, QuickDraw
• Inside Macintosh, Volume II, The Printing Manager
• LaserWriter Reference Manual
• Technical Note M.IM.OffscreenBitMap—
Drawing Into An Off-Screen Bitmap
• Technical Note M.IM.PictComments —
Optimizing for the LaserWriter—Picture Comments
• Technical Note M.IM.Spooler —
Effect of Spool-A-Page/Print-A-Page on Shared Printers
• Technical Note M.IM.PrintLoop —
A Printing Loop That Cares…
• PostScript Language Reference, Adobe Systems, Incorporated
• PostScript Language Tutorial and Cookbook, Adobe Systems, Incorporated
MacDraw is a registered trademark of Claris Corporation.
PostScript is a registered trademark of Adobe Systems, Incorporated.
PR 5 - LaserWriter ROMs Bugs
Printing
Revised by: Ginger Jernigan July 1987
Updated: March 1988
Written by: Ginger Jernigan May 1987
These are LaserWriter bugs that your users may encounter when printing from any Macintosh application. These are for your information; you cannot code around them. The bugs described here occur in the 1.0 and 2.0 LaserWriter ROMs.
To determine which ROMs their LaserWriter contains, users can look at the test page that the LaserWriter prints at start-up time. In addition to other information (detailed in the LaserWriter user’s manual), the ROM version is shown at the bottom of the line graph. The original LaserWriter contained version 1.0 ROMs. The currently shipping LaserWriter and those upgraded to the LaserWriter Plus contain version 2.0 ROMs.
These are some of the problems we know of:
1. If the level of paper in the paper tray is getting low, and the user prints a document that will cause the tray to become empty, a PostScript error may occur. This problem exists in both the 1.0 and 2.0 LaserWriter ROMs and will not be fixed in the next ROM version.
2. If a user prints more than 15 copies of a document, a timeout condition may occur causing the print job to abort. With LaserShare, this problem can occur with as few as 9 copies. This problem is a result of the LaserWriter turning AppleTalk off while it is printing. It doesn’t send out any packets to tell the world it’s still alive while it is printing, so the connection times out after about 2 minutes. This problem exists in both the 1.0 and 2.0 LaserWriter ROMs and will not be fixed in the next ROM version.
3. When printing a document that contains more than 10 patterns, users may receive intermittent PostScript errors. This usually occurs when trying to print a lot of patterns, and a bitmap image on the same page. The code for imaging patterns allocates almost all of the available RAM for itself, so when the bitmap imaging code tries to allocate space, and there isn’t enough (and it doesn’t know how to reclaim memory from the previous operation), a limitcheck error occurs. This problem exists in 2.0 LaserWriter ROMs. It will be improved but not fixed in the next ROM version.
4. If a user chooses US Letter or B5 paper and has a different sized tray in the printer, and prints using manual feed, the LaserWriter will print assuming that the paper being fed manually is the same size as that in the tray. For example, if they have a US letter tray in the LaserWriter and print a document formatted for B5 letter using manual feed, the image will not be centered on the page. The printer assumes that the manually fed paper is also US letter size and prints the image positioned accordingly, despite the driver’s instructions. This is a bug in the Note operator in PostScript, which the driver uses for specifying the US letter and B5 letter paper sizes. The workaround is to tell the user to put an B5 tray in the printer when printing B5 manually. This problem exists in the 1.0 and 2.0 ROMs and will not be fixed in the next ROM version.
By the way, an interesting, but annoying, occurance of this bug happens when manually printing Legal sized documents with the 4.0 LaserWriter driver. When the Larger Print Area option in the style dialog is deselected (which is the default) the driver uses the Note operator to specify the page size. When the user prints the document using manual feed, and has a US letter tray in the printer, the image is shifted up on the page cutting off the top of the image. If you tell the user to turn on the Larger Print Area option in the style dialog, the driver specifies the page size using Legal instead of Note and the image is printed properly.
Further Reference:
• The Printing Manager
• PostScript Language Reference Manual, Adobe Systems
PR 6 - Using Low-Level Printing Calls With AppleTalk ImageWriters
Printing
Revised by: Scott "ZZ" Zimmerman February 1988
Revised by: March 1988
Written by: Ginger Jernigan May 1987
When you use the low-level printer driver to print, you don’t get the benefits of the error checking that is done when you use the high-level Printing Manager. So, if the user prints to an AppleTalk ImageWriter (including an AppleTalk ImageWriter LQ) that is busy printing another job, the driver doesn’t know whether the printer is busy, offline, or disconnected. Because of this, PrError will return (and PrintErr will contain) abortErr.
Since there is no way to tell when you are printing to an AppleTalk ImageWriter, the only workaround for this is to use high-level Printing Manager interface.
Further Reference:
• The Printing Manager
PR 7 - PrGeneral
Printing
Revised by: March 1988
Written by: Ginger Jenigan May 1987
The Printing Manager architecture has been expanded to include a new procedure called PrGeneral. The features described here are advanced, special-purpose features, intended to solve specific problems for those applications that need them. The calls to determine printer resolution introduce a good deal of complexity into the application’s code, and should be used only when necessary.
Version 2.5 (and later) of the ImageWriter driver and version 4.0 (and later) of the LaserWriter driver implement a generic Printing Manager procedure called PrGeneral. This procedure allows the Print Manager to expand in functionality, by allowing printer drivers to implement various new functions. The Pascal declaration of PrGeneral is:
PROCEDURE PrGeneral (pData: Ptr);
The pData parameter is a pointer to a data block. The structure of the data block is declared as follows:
TGnlData = RECORD {1st 8 bytes are common for all PrGeneral calls)
iOpCode : INTEGER; {input}
iError : INTEGER; {output}
lReserved : LONGINT; {reserved for future use}
{more fields here, depending on particular call}
END;
The first field is a 2-byte opcode, iOpCode, which acts like a routine selector. The currently available opcodes are described below.
The second field is the error result, iError, which is returned by the print code. This error only reflects error conditions that occur during the PrGeneral call. For example, if you use an opcode that isn’t implemented in a particular printer driver then you will get a OpNotImpl error.
Here are the errors currently defined:
CONST
noErr = 0; {everything’s hunky}
NoSuchRsl = 1; {the resolution you chose isn’t available}
OpNotImpl = 2; {the driver doesn’t support this opcode}
After calling PrGeneral you should always check PrError. If noErr is returned, then you can proceed. If ResNotFound is returned, then the current printer driver doesn’t support PrGeneral and you should proceed appropriately. See Technical Note #118 for details on checking errors returned by the Printing Manager.
IError is followed by a four byte reserved field (that means don’t use it). The contents of the rest of the data block depends on the opcode that the application uses. There are currently five opcodes used by the ImageWriter and LaserWriter drivers.
The Opcodes
Initially, the following calls are implemented via PrGeneral:
• GetRslData (get resolution data): iOpCode = 4
• SetRsl (set resolution): iOpCode = 5
• DraftBits (bitmaps in draft mode): iOpCode = 6
• noDraftBits (no bitmaps in draft mode): iOpCode = 7
• GetRotn (get rotation): iOpCode = 8
The GetRslData and SetRsl allow the application to find out what physical resolutions the printer supports, and then specify a supported resolution. DraftBits and noDraftBits invoke a new feature of the ImageWriter, allowing bitmaps (imaged via CopyBits) to be printed in draft mode. GetRotn lets an application know whether landscape has been selected. Below is a detailed description of how each routine works.
The GetRslData Call
GetRslData (iOpCode = 4) returns a record that lets the application know what resolutions are supported by the current printer. The application can then use SetRsl (description follows) to tell the printer driver which one it will use. This is the format of the input data block for the GetRslData call:
TGetRslBlk = RECORD {data block for GetRslData call}
iOpCode: Integer; {input; = getRslDataOp}
iError: Integer; {output}
lReserved: LongInt; {reserved for future use}
iRgType: Integer; {output; version number}
XRslRg: TRslRg; {output; range of X resolutions}
YRslRg: TRslRg; {output; range of Y resolutions}
iRslRecCnt: Integer; {output; how many RslRecs follow}
rgRslRec: ARRAY[1..27] OF TRslRec; {output; number filled depends on
printer type}
END;
The iRgType field is much like a version number; it determines the interpretation of the data that follows. At present, a iRgType value of 1 applies both to the LaserWriter and to the ImageWriter.
For variable-resolution printers like the LaserWriter, the resolution range fields XRslRg and YRslRg express the ranges of values to which the X and Y resolutions can be set. For discrete-resolution printers like the ImageWriter, the values in the resolution range fields are zero.
Note: In general, X and Y in these records are the horizontal and vertical directions of the printer, not the document! In landscape orientation, X is horizontal on the printer but vertical on the document.
After the resolution range information there is a word which gives the number of resolution records that contain information. These records indicate the physical resolutions at which the printer can actually print dots. Each resolution record gives an X value and a Y value.
When you call PrGeneral you pass in a data block that looks like this:
Below is the data block returned for the LaserWriter:
Note that all the resolution range numbers happen to be the same for this printer. There is only one resolution record, which gives the physical X and Y resolutions of the printer (300x300).
Below is the data block returned for the ImageWriter.
All the resolution range values are zero, because only discrete resolutions can be specified for this printer. There are four resolution records giving these discrete physical resolutions.
Note that GetRslData always returns the same information for a particular printer type—it is not dependent on what the user does or on printer configuration information.
The SetRsl Call
SetRsl (iOpCode = 5) is used to specify the desired imaging resolution, after using GetRslData to determine a workable pair of values. Below is the format of the data block:
TSetRslBlk = RECORD {data block for SetRsl call}
iOpCode: Integer; {input; = setRslOp}
iError: Integer; {output}
lReserved: LongInt; {reserved for future use}
hPrint: THPrint; {input; handle to a valid print record}
iXRsl: Integer; {input; desired X resolution}
iYRsl: Integer; {input; desired Y resolution}
END;
hPrint should be the handle of a print record that has previously been passed to PrValidate. If the call executes successfully, the print record is updated with the new resolution; the data block comes back with 0 for the error and is otherwise unchanged.
However, if the desired resolution is not supported, the error is set to noSuchRsl and the resolution fields are set to the printer’s default resolution
Note that you can undo the effect of a previous call to SetRsl by making another call that specifies an unsupported resolution (such as 0x0), forcing the default resolution.
The DraftBits Call
DraftBits (iOpCode = 6) is implemented on both the ImageWriter and the LaserWriter. (On the LaserWriter it does nothing, since the LaserWriter is always in draft mode and can always print bitmaps.) Below is the format of the data block:
TDftBitsBlk = RECORD {data block for DraftBits and NoDraftBits calls}
iOpCode: Integer; {input; = draftBitsOp or noDraftBitsOp}
iError: Integer; {output}
lReserved: LongInt; {reserved for future use}
hPrint: THPrint; {input; handle to a valid print record}
END;
hPrint should be the handle of a print record that has previously been passed to PrValidate.
This call forces draft-mode (i.e., immediate) printing, and will allow bitmaps to be printed via CopyBits calls. The virtue of this is that you avoid spooling large masses of bitmap data onto the disk, and you also get better performance.
The following restrictions apply:
• This call should be made before bringing up the print dialogs because it affects their appearance. On the ImageWriter, calling DraftBits disables the landscape icon in the Style dialog, and the Best, Faster, and Draft buttons in the Job dialog.
• If the printer does not support draft mode, already prints bitmaps in draft mode, or does not print bitmaps at all, this call does nothing.
• Only text and bitmaps can be printed.
• As in the normal draft mode, landscape format is not allowed.
• Everything on the page must be strictly Y-sorted, i.e. no reverse paper motion between one string or bitmap and the next. Note that this means you can’t have two or more objects (text or bitmaps) side by side; the top boundary of each object must be no higher than the bottom of the preceding object.
The last restriction is important. If you violate it, you will not like the results. But note that if you want two or more bitmaps side by side, you can combine them into one before calling CopyBits to print the result. Similarly, if you are just printing bitmaps you can rotate them yourself to achieve landscape printing.
The NoDraftBits Call
NoDraftBits (iOpCode = 7) is implemented on both the ImageWriter and the LaserWriter. (On the LaserWriter it does nothing, since the LaserWriter is always in draft mode and can always print bitmaps.) The format of the data block is the same as that for the DraftBits call.
This call cancels the effect of any preceding DraftBits call. If there was no preceding DraftBits call, or the printer does not support draft-mode printing anyway, this call does nothing.
The GetRotn Call
GetRotn (iOpCode = 8) is implemented on the ImageWriter and LaserWriter. Here is the format of the data block:
TGetRotnBlk = RECORD {data block for GetRotn call}
iOpCode: Integer; {input; = getRotnOp}
iError: Integer; {output}
lReserved: LongInt; {reserved for future use}
hPrint: THPrint; {input; handle to a valid print record}
fLandscape: Boolean; {output; Boolean flag}
bXtra: SignedByte; {reserved}
END;
hPrint should be the handle to a print record that has previously been passed to PrValidate.
If landscape orientation is selected in the print record, then fLandscape is true.
How To Use The PrGeneral Opcodes
The SetRsl and DraftBits calls may require the print code to suppress certain options in the Style and/or Job dialogs, therefore they should always be called before any call to the Style or Job dialogs. An application might use these calls as follows:
• Get a new print record by calling PrintDefault, or take an existing one from a document and call PrValidate on it.
• Call GetRslData to find out what the printer is capable of, and decide what resolution to use. Check PrError to be sure the PrGeneral call is supported on this version of the print code; if the error is ResNotFound, you have older print code and must print accordingly. But if the PrError return is 0, proceed:
• Call SetRsl with the print record and the desired resolution if you wish.
• Call DraftBits to invoke the printing of bitmaps in draft mode if you wish.
Note that if you call either SetRsl or DraftBits, you should do so before the user sees either of the printing dialogs.
Further Reference:
• The Printing Manager
PR 8 - PrGeneral Bug
Printing
Revised by: March 1988
Written by: Scott "ZZ" Zimmerman November 1987
This technical note documents a bug in the implementation of the PrGeneral procedure in the LaserWriter driver version 4.0. The bug has to do with the format of the information returned by the GetRslData opcode. This technical note will also describe a workaround for the problem.
One of the opcodes supported by the PrGeneral procedure (Technical Note #128) is named GetRslData. The GetRslData operation initializes a resolution record that is of the following form:
TRslRg = RECORD {used in TGetRslBlk}
iMin: Integer; {0 if printer only supports discrete resolutions}
iMax: Integer; {0 if printer only supports discrete
resolutions}
END;
TRslRec = RECORD {used in TGetRslBlk}
iXRsl: Integer; {a discrete, physical X resolution}
iYRsl: Integer; {a discrete, physical Y resolution}
END;
TGetRslBlk = RECORD {data block for GetRslData call}
iOpCode: Integer; {input; = getRslDataOp}
iError: Integer; {output}
lReserved: LongInt; {reserved for future use}
iRgType: Integer; {output; this declaration is for RgType1}
XRslRg: TRslRg; {output; range of X resolutions}
YRslRg: TRslRg; {output; range of Y resolutions}
iRslRecCnt: Integer; {output; how many RslRecs follow}
rgRslRec: ARRAY[1..27]
OF TRslRec; {output; number used depends on printer type}
END;
The LaserWriter 4.0 implementation has a bug that affects the YRslRg and XRslRg fields of the TGetRslBlk record. The correct values for the fields are:
TGetRslBlk.XRslRg.iMin := 25;
TGetRslBlk.XRslRg.iMax := 1500;
TGetRslBlk.YRslRg.iMin := 25;
TGetRslBlk.YRslRg.iMax := 1500;
Unfortunately, the information returned by the LaserWriter 4.0 version of PrGeneral is:
TGetRslBlk.XRslRg.iMin := 25;
TGetRslBlk.XRslRg.iMax := 25;
TGetRslBlk.YRslRg.iMin := 1500;
TGetRslBlk.YRslRg.iMax := 1500;
The recommended workaround for this problem is to use the PrDrvrVers function (Inside Macintosh II-163) to find out which version of the print driver you are using. If you are using 4.0, modify the resolution data before using it. The following code fragment illustrates this workaround:
When the bug is fixed in a future version of the driver, the CheckRslRecord procedure will no longer have any effect on the resolution record. This will make sure your application gets the correct resolution data no matter which version of the driver is being used.
Further Reference:
• The Print Manager
• Technical Note M.IM.PrGeneral—
PrGeneral
PR 9 - Print Dialogs: Adding Items
Printing
Revised by: March 1988
Written by: Ginger Jernigan November 1986
Lew Rollins
This technical note discusses how to add your own items to the Printing Manager’s dialogs.
When the Printing Manager was initially designed, great care was taken to make the interface to the printer drivers as generic as possible in order to allow applications to print without being device-specific. There are times, however, when this type of non-specific interface interferes with the flexibility of an application. An application may require additional information before printing which is not part of the general Printing Manager interface. This technical note describes a method that an application can use to add its own items to the existing style and job dialogs.
Before continuing, you need to be aware of some guidelines that will increase your chances of being compatible with the printing architecture in the future:
• Only add items to the dialogs as described in this technical note. Any other methods will decrease your chances of survival in the future.
• Do not change the position of any item in the current dialogs. This means don’t delete items from the existing item list or add items in the middle. Add items only at the end of the list.
• Don’t count on an item retaining its current position in the list. If you depend on the Draft button being a particular number in the ImageWriter’s style dialog item list, and we change the Draft button’s item number for some reason, your program may no longer function correctly.
• Don’t use more than half the screen height for your items. Apple reserves the right to expand the items in the standard print dialogs to fill the top half of the screen.
• If you are adding lots of items to the dialogs (which may confuse users), you should consider having your own separate dialog in addition to the existing Printing Manager dialogs.
The Heart
Before we talk about how the dialogs work, you need to know this: at the heart of the printer dialogs is a little-known data structure partially documented in the MacPrint interface file. It’s a record called TPrDlg and it looks like this:
TPrDlg = RECORD {Print Dialog: The Dialog Stream object.}
dlg : DialogRecord; {dialog window}
pFltrProc : ProcPtr; {filter proc.}
pItemProc : ProcPtr; {item evaluating proc.}
hPrintUsr : THPrint; {user’s print record.}
fDoIt : BOOLEAN;
fDone : BOOLEAN;
lUser1 : LONGINT; {four longs reserved by Apple}
lUser2 : LONGINT;
lUser3 : LONGINT;
lUser4 : LONGINT;
iNumFst : INTEGER; {numeric edit items for std filter}
iNumLst : INTEGER;
{... plus more stuff needed by the particular printing dialog.}
END;
TPPrDlg = ^TPrDlg; {== a dialog ptr}
All of the information pertaining to a print dialog is kept in the TPrDlg record. This record will be referred to frequently in the discussion below.
How the Dialogs Work
When your application calls PrStlDialog and PrJobDialog, the printer driver actually calls a routine called PrDlgMain. This function is declared as follows:
FUNCTION PrDlgMain (hprint: THPrint; pDlgInit: ProcPtr): BOOLEAN;
PrDlgMain first calls the pDlgInit routine to set up the appropriate dialog (in Dlg), dialog hook (pItemProc) and dialog event filter (pFilterProc) in the TPrDlg record (shown above). For the job dialog, the address of PrJobInit is passed to PrDlgMain. For the style dialog, the address of PrStlInit is passed. These routines are declared as follows:
FUNCTION PrJobInit (hPrint: THPrint): TPPrDlg;
FUNCTION PrStlInit (hPrint: THPrint): TPPrDlg;
After the initialization routine sets up the TPrDlg record, PrDlgMain calls ShowWindow (the window is initially invisible), then it calls ModalDialog, using the dialog event filter pointed to by the pFltrProc field. When an item is hit, the routine pointed to by the pItemProc field is called and the items are handled appropriately. When the OK button is hit (this includes pressing Return or Enter) the print record is validated. The print record is not validated if the Cancel button is hit.
How to Add Your Own Items
To modify the print dialogs, you need to change the TPrDlg record before the dialog is drawn on the screen. You can add your own items to the item list, replace the addresses of the standard dialog hook and event filter with the addresses of your own routines and then let the dialog code continue on its merry way.
TPrDlg record for you and return a pointer to that record. Then call PrDlgMain directly, passing in the address of your own initialization function. The example code’s initialization function adds items to the dialog item list, saves the address of the standard dialog hook (in our global variable prPItemProc) and puts the address of our dialog hook into the pItemProc field of the TPrDlg record. Please note that your dialog hook must call the standard dialog hook to handle all of the standard dialog’s items.
Note: If you wish to have an event filter, handle it the same way that you do a dialog hook.
Now, here is an example (written in MPW Pascal) that modifies the job dialog. The same code works for the style dialog if you globally replace ‘Job’ with ‘Stl’. Also included is a function (AppendDITL) provided by Lew Rollins (originally written in C, translated for this technical note to MPW Pascal) which demonstrates a method of adding items to the item list, placing them in an appropriate place, and expanding the dialog window’s rectangle.
UnloadSeg(@_DataInit); {remove data initialization code before any allocations}
InitGraf(@thePort);
InitFonts;
FlushEvents(everyEvent,0);
InitWindows;
InitMenus;
TEInit;
InitDialogs(NIL);
InitCursor;
{ call the routine that does printing }
FirstBoxValue := 0; { value of our first additional box }
SecondBoxValue := 0; { value of our second addtl. box }
PrOpen; { Open the Print Manager }
IF PrError = noErr THEN
err := Print { This actually brings up the modified Job dialog }
ELSE BEGIN
{tell the user that PrOpen failed}
END;
PrClose; { Close the Print Manager and leave }
END.
The Lightspeed C Example Program
/* NOTE: Apple reserves the top half of the screen (where the current DITL
items are located). Applications may use the bottom half of the screen to add items, but should not change any items in the top half of the screen. An application should expand the print dialogs only as much as is absolutely necessary.
*/
/* Note: A global search and replace of 'Job' with 'Stl' will produce
code that modifies the style dialogs */
#include <DialogMgr.h>
#include <MacTypes.h>
#include <Quickdraw.h>
#include <ResourceMgr.h>
#include <WindowMgr.h>
#include <pascal.h>
#include <printmgr.h>
#define nil 0L
static TPPrDlg PrtJobDialog; /* pointer to job dialog */
/* This points to the following structure:
struct {
DialogRecord Dlg; (The Dialog window)
ProcPtr pFltrProc; (The Filter Proc.)
ProcPtr pItemProc; (The Item evaluating proc. -- we'll change this)
/* Now comes the part where we patch in our item handler. We have to save
the old item handler address, so we can call it if one of the standard items is hit, and put our item handler's address in pItemProc field of the TPrDlg struct
*/
prPItemProc = (long)PrtJobDialog->pItemProc;
/* Now we'll tell the modal item handler where our routine is */
PrtJobDialog->pItemProc = (ProcPtr)&MyJobItems;
/* PrDlgMain expects a pointer to the modified dialog to be returned.... */
/* invert value of SecondBoxValue and redraw it */
SecondBoxValue ^= 1;
SetCtlValue(itemH,SecondBoxValue);
break;
default: Debugger(); /* OH OH */
} /* switch */
} /* if (myItem > 0) */
else /* chain to standard item handler, whose address is saved in
prPItemProc */
{
CallPascal(theDialog,itemNo,prPItemProc);
}
} /* MyJobItems */
The Rez Source
#include "types.r"
resource 'DITL' (256) {
{ /* array DITLarray: 2 elements */
/* [1] */
{8, 0, 24, 112},
CheckBox {
enabled,
"First Box"
};
/* [2] */
{8, 175, 24, 287},
CheckBox {
enabled,
"Second Box"
}
}
};
Further Reference:
• The Printing Manager
• The Dialog Manager
PR 10 - A Printing Loop That Cares…
Printing
Revised by: Pete “Luke” Alexander October 1990
Written by: Ginger Jernigan September 1987
This Technical Note discusses opening and closing the Printing Manager with calls to _PrOpen and _PrClose as well as how to handle errors at print time.
Changes since October 1989: Added the section on error checking, incorporating the error code descriptions formerly found in Technical Note #72, Optimizing For The LaserWriter—Techniques and an updated version of the information formerly found in Technical Note #118, How To Check and Handle Printing Errors.
Introduction
At one time, Apple recommended that developers call _PrOpen at the beginning of their application and _PrClose at the end, before returning to the Finder. This recommendation was in the ancient past when an application only had to deal with a single printer driver.
As more printer drivers became available, it became important for an application to consider the presence of other applications and how opening and closing the printer driver affected them. The user could open the Chooser at any time and change the current printer driver without the current application’s knowledge. If an application followed the old philosophy and a user changed the current printer driver while running the application, the next time the user attempted to print, the wrong driver would be open, the Printing Manager would not be able to find the necessary resources, and the user would get an error.
The Current Recommendation
DTS currently recommends that applications open and close the printer driver each time the application uses the Printing Manager.
Restore the resource file to the printer driver's.
**/
UseResFile(printmgrsResFile);
thePrPort = PrOpenDoc(thePrRecHdl, nil, nil);
if (PrError() == noErr)
{
/**
Print the range of pages of the document
requested by the user from the Print Job Dialog.
**/
pageNumber = firstPage;
while (pageNumber <= lastPage && PrError() == noErr)
{
PrOpenPage(thePrPort, nil);
if (PrError() == noErr)
{
/**
rPage (IM II-150) is the printable area
for the currently selected printer. By passing the current port to the draw routine, enables your app to use the same routine to draw to the screen and the printer's GrafPort.
**/
DrawStuff ((**thePrRecHdl).prInfo.rPage,
(GrafPtr) thePrPort, pageNumber);
}
PrClosePage(thePrPort);
pageNumber++;
} /** End pageNumber loop **/
}
PrCloseDoc(thePrPort);
} /** End copies loop **/
}
/**
The printing job is being canceled by the request of the
user from the Print Style Dialog or the Print Job Dialog.
PrError will be set to PrAbort to tell the Print Manager to
abort the current printing job.
**/
else
PrSetError (iPrAbort); /** cancel from the job dialog **/
}
else
PrSetError (iPrAbort); /** cancel from the style dialog **/
}
}
if (((**thePrRecHdl).prJob.bJDocLoop == bSpoolLoop) && (PrError() == noErr))
Grab the printing error before you close the Print Manager
and the error disappears.
**/
PrintError = PrError();
PrClose();
/**
You do not want to report any printing errors until you have fallen
through the printing loop. This will make sure that ALL of the Print
Manager's open calls have their corresponding close calls, thereby
enabling the Print Manager to close properly and that all temporary
memory allocations are released.
**/
if (PrintError != noErr)
PostPrintingErrors (PrintError);
}
if (thePrRecHdl != nil)
DisposHandle((Handle) thePrRecHdl);
if (PrintingStatusDialog != nil)
DisposDialog(PrintingStatusDialog);
SetPort(oldPort);
} /** PrintStuff **/
Checking And Handling Printing Errors
An application should always check for error conditions while printing by calling PrError. PrError returns errors from the Printing Manager (and some AppleTalk and OS errors) that occur during printing.
As the example code demonstrates, an application should call PrError after each call to a Printing Manager function or procedure. By consistently checking PrError after each call, the application is able to catch any errors created at print time and report them to a user via a dialog box in a clean and graceful manner.
The following section outlines some general error-handling guidelines.
• You should avoid calling PrError within your pIdle procedure; errors that occur while it is executing are usually temporary and serve only as internal flags for communication within the printer driver—they are not intended for the application. If you absolutely must call PrError within your idle procedure, and an error occurs, never abort printing within the idle procedure itself. Wait until the last called printing procedure returns, then check to see if the error still remains. Attempting to abort printing within an idle procedure is a guarantee of certain death.
• Upon detecting an error after the completion of a printing routine, stop drawing at that point, and proceed to the next procedure to close any previously made open calls. For example, if you detect an error after calling PrOpenDoc, skip to the next PrCloseDoc. Or, if you get an error after calling PrOpenPage, skip to the next PrClosePage and PrCloseDoc. Remember that if you have called PrOpen, then you must call the corresponding PrClose to ensure that printing closes properly and all temporary memory allocations are released and returned to the heap.
• Do not display any alert or dialog boxes to report an error until the end of the printing loop. Once at the end, check for the error again; if there is no error assume that printing completed normally. If the error is still present, then you can alert the user.
This technique is important for two reasons. First, if you display a dialog box in the middle of the printing loop, it could cause errors that can terminate an otherwise normal job. For example, if the printer is an AppleTalk printer, the connection can be terminated abnormally since the driver would be unable to respond to AppleTalk requests received from the printer while the dialog box was waiting for input from the user. If the printer does not hear from the Macintosh with a short period of time (e.g., 30 seconds), it times out, assuming that the Macintosh is no longer there, which results in a prematurely broken connection causing another error to which the application must respond.
In addition, the driver may have already displayed its own dialog box in response to an error. In this instance, the driver posts an error to let the application know that something went wrong and it should abort printing. For example, when the LaserWriter driver detects that the Laser Prep version which has been downloaded to the LaserWriter is different than that with which the user is trying to print, it displays the appropriate dialog box informing the user of the situation and giving him the option of reinitializing the printer. If the user chooses to cancel printing, the driver posts an error to let the application know that it needs to abort, but since the driver has already taken care of the error by displaying a dialog box, the error is reset to zero before the printing loop is complete. The application should check for the error again at the end of the printing loop, and if it still indicates an error, the application can then display the appropriate dialog box.
• If using PrGeneral, be prepared to receive the following errors: NoSuchRsl, OpNotImpl, and resNotFound. In all three cases, the application should be prepared to continue to print without using the features of that particular opcode.
However, in the case of the resNotFound error, it means the current printer driver does not support PrGeneral. This lack of support should not be a problem for an application, but it needs to be prepared to deal with this error. If you receive a resNotFound error from PrError, clear the error with a call to PrSetError(0); otherwise, PrError might still contain this error the next time you check it, which would prevent your application from printing.
Canceling or Pausing the Printing Process
If you install a procedure for handling requests to cancel printing, with an option to pause the printing process, beware of timeout problems when printing to the LaserWriter. Communication between the Macintosh and the LaserWriter must be maintained to prevent a job or a wait timeout. If there is no communication for a period of time (over two minutes), the printer times out and the print job terminates due to a wait timeout. Or, if the print job requires more than three minutes to print, the print job terminates due to a job timeout. Since, there is no good method to determine to what type of printer an application is printing, it is probably a good idea to document the possibility of a LaserWriter timing out for a user who chooses to select “pause” for over two minutes.
Error Messages Created In Print Land…
The Printing Manager reports the error messages covered in this section. If an error that does not belong to the Printing Manager occurs, the Printing Manager puts it into low memory, where it can be retrieved with a call to PrError, and terminates the printing loop, if necessary. As already documented, if you encounter an error in the middle of a printing loop, do not jump out; fall through the loop and let the Printing Manager terminate properly.
Error Code Constant Description
0 noErr No error
128 iPrAbort Abort the printing process
(Command-period)
-1 iPrSavePFil Problem saving print file
-17 controlErr Unimplemented Control call
-27 iIOAbort I/O problems
-108 iMemFullErr Not enough heap space
The following errors are specific to the LaserWriter family:
-4101 Printer not found or closed
-4100 Connection just closed
-4099 Write request too big
-4098 Request already active
-4097 Bad connection refnum
-4096 No free Connect Control Blocks (CCBs) available
-8133 PostScript error occurred during transmission of data to printer. Most often caused by a bug in the PostScript code being downloaded.
-8132 Timeout occurred. This error is returned when no data has been sent to the printer for two minutes. Usually caused by extremely long imaging time.
-8131 Printer not responding: it may have been turned “off.” This error occurs if a user turns off the LaserWriter in the middle of a print job.
The following errors are specific to PrGeneral:
1 NoSuchRsl Requested resolution is not supported
2 OpNotImpl Requested PrGeneral opcode not implemented in the current printer driver.
-192 resNotfound The current printer driver does not support PrGeneral.
The most common error encountered is -4101, which is generated if no LaserWriter is selected. Since this error is so common, it is a good idea to display a dialog box requesting the user to select a printer from the Chooser when this error is encountered.
Further Reference:
• Inside Macintosh, Volume II-145 & V-410, The Printing Manager
• Technical Note M.IM.DevIndPrinting —
Device-Independent Printing
• d e v e l o p, July 1990, Issue 3, “Meet PrGeneral”
PR 11 - Programmatic PostScript Files
Printing
Written by: Matt Deatherage & Hugo Ayala September 1992
This Technical Note discusses how to make the LaserWriter driver, versions 7.0 and later, create PostScript files from your printing loop, and when this is and is not appropriate.
Topics
• Creating PostScript files
• The LaserWriter driver is not a PostScript conversion utility
• Sample functions for creating an appropriate print record
I get to make PostScript files?
Yes, but don’t get too excited yet. The feature we’re about to describe is not applicable in every case where you might want to generate PostScript code, so it’s not a generic solution. Like those standardized tests used to say, “Please read all the directions before marking the page.”
There are definitely situations where forcing the LaserWriter driver to create a PostScript file has advantages. It’s extremely handy for many large, multi-network Macintosh installations that have unique printing needs. It can be handy for people using non-AppleTalk PostScript printers. It’s a very useful capability in this and dozens of other situations.
What it’s not useful for is generic PostScript conversion.
The LaserWriter driver was designed expressly for the purpose of converting QuickDraw calls to PostScript for high-resolution imaging on Apple’s PostScript printers. As the years have gone by, it’s been adapted to things that weren’t part of the original design model (like driving non-Apple PostScript printers). One of the features added in recent years was the capability to dump a PostScript file when printing. This was originally only a debugging feature for the convenience of the engineers working on the driver, but when it was made accessible to users through a hidden dialog item and a not-so-secret key combination, the response was overwhelming. In response, the printing engineers included the capability to pick a PostScript file for output was built into versions 7.0 and later of the driver.
That didn’t change the fact that it was still, at heart, a glorified debugging feature. The PostScript the driver creates is designed expressly for printing. It does not create Encapsulated PostScript files (EPSF format). It doesn’t easily incorporate any PostScript document conventions other than the one it uses for its own benefit. It doesn’t generate PostScript Level 2. It includes large amounts of custom encrypted code to provide TrueType™ font rendering technology on PostScript printers where possible.
It’s important to understand that you can’t change any of this. If you have a strong need to programmatically create files just like the LaserWriter driver would create if you chose the “PostScript file” radio button in the job dialog, this technique is for you. If you need anything more customizable, or more specialized, or in a different format, this Note is not for you. The LaserWriter driver is not and never has been a generic PostScript conversion utility; if you want such capabilities, you must still build them into your application.
How to Do the Deed
The LaserWriter driver stores the PostScript file information in the print record—but not in a format designed for your easy access. To make it print to a PostScript file, you have to set a bit, fill in a vRefNum , a dirID and a pointer to the file name—all inside a private LaserWriter driver structure.
The flag is a bit in the word starting at offset $52 (or 82 decimal) of the print record. This is documented as boolean value f2 of the printer flags record prFlag1. Figure 1 shows a redeclaration of this record with the newly-documented fPSFile bit included.
lwTPrFlag1 = PACKED RECORD
f15: BOOLEAN;
f14: BOOLEAN;
f13: BOOLEAN;
f12: BOOLEAN;
f11: BOOLEAN;
f10: BOOLEAN;
f9: BOOLEAN;
f8: BOOLEAN;
f7: BOOLEAN;
f6: BOOLEAN;
f5: BOOLEAN;
f4: BOOLEAN;
f3: BOOLEAN;
fPSFile: BOOLEAN;
fLstPgFst: BOOLEAN;
fUserScale: BOOLEAN;
END;
Figure 1—Redefined PrFlag1 Record
Note that the record is renamed beginning with lw to emphasize that this technique only works with the LaserWriter driver. This means you have to check that you’re printing to the LaserWriter driver by making sure the high byte of TPRStl.wDev is 3, for the LaserWriter driver. If other drivers impersonate the LaserWriter driver (which they may do, for example, so that your application sends them PostScript in picture comments), this may crash. Unfortunately, Apple can’t conclusively tell you that a third-party printer driver will or will not support this feature. Apple’s driver does.
Where does the LaserWriter driver put the new PostScript file? The word starting at offset $68 (104 decimal) is the vRefNum for the file to save, and the long at offset $70 is the directory ID for where to store the file. The long starting at $6A (or 106 decimal in the print record) is a pointer to the PostScript file name as a Pascal string—just the file name; the other fields indicate the other portions of the file’s specification (in fact, they’re all three portions of an FSSpec record).
However, the name pointer isn’t just any old pointer. There’s some baggage with it.
First, the LaserWriter driver always assumes that it allocated the pointer, and somewhere in the printing loop, when it’s done with the PostScript file name, it calls DisposPtr on that pointer. That means you must allocate it with NewPtr and it must be valid during the print loop, or your heap becomes toast. It should be a 64-byte pointer, because the driver assumes that’s what it is.
Second, the LaserWriter driver assumes that it’s the only one who would be messing with this private field in the print record, so it only clears it during PrJobInit. That means when you're done printing, your print record will still contain a pointer to the memory that used to point to the file name string. If you save this print record and try to print again without going through the job dialog, the LaserWriter driver will use it as a pointer on your behalf, even though it probably doesn’t point to anything useful anymore. Also, if the pointer is non-NIL entering the job dialog (if somehow someone bypasses PrJobInit), it also assumes it’s a pointer and uses it. So, before you save or use this print record for anything, always zero the file name pointer. It will save many hassles down the road.
Third, if you encounter a print record that has the fPSFile bit (bit 3 of offset $52) already set, the LaserWriter driver has already validated the information in the print record. The pointer has been allocated and the directory information stored. Do not override these values or you may cause a memory leak.
The driver also saves in the print record, in the next two bytes starting at $6E, the vRefNum of the last volume used to store a PostScript file. It also uses the directory ID field at offset $70 to indicate the last directory used for storing a PostScript file. This is only so the Standard File dialog put up by PrJobDialog can start with the most appropriate directory; it’s not important to programmatic control of this mechanism.
Figure 2 shows the LaserWriter driver’s version of the print record, redeclared with the newly-documented information as lwTHPrint:
TYPE
{ Print Job: Print "form" for a single print request. }
When you print in the foreground, the LaserWriter driver uses the information in the print record to create the PostScript file. When you print in the background, the LaserWriter driver adds a resource with the information to the spool file so PrintMonitor can do the same thing. This lets this method work in both the foreground and background cases.
Be careful about picking file names to use—whatever method you use, if the file name is not unique, the LaserWriter driver will attempt to delete the existing file and cause you, as Hugo puts it, “endless grief.” Simply examining the directory at spooling time isn’t enough if there’s any chance the file name might not be unique when PrintMonitor gets around to despooling the file. Furthermore, if you try to write over an existing file, the LaserWriter driver will not change the end-of-file position, so the result file will have the larger EOF of the new file and the old contents. This is also Bad.
Here’s a summary of what we’ve discussed:
1. Get a print record ready to start your printing loop.
2. Change the fPSFile bit to tell the LaserWriter driver to create a PostScript file instead.
3. Set the vRefNum and directory ID for where you want to store the file.
4. Allocate a 64-byte pointer to store the file name; put the filename in the new memory block and put the pointer in the print record.
5. Complete your print loop as normal.
6. Zero the file name pointer in the print record as a precaution.
7. Save or otherwise manipulate the print record for future use.
Samples? Sure.
The following System 7-only sample routine PrPSFileDialog can be used in place of PrJobDialog when using this technique. Note that the routine requires a handle of type lwTHPrint.
Note: Be careful about bypassing the job dialog. The LaserWriter driver resets some other job-specific parameters (besides the file to save PostScript in) in PrJobMain. If you save a print record immediately after calling PrJobDialog and use that print record without going through the job dialog again, all of the job-specific parameters will be preserved (number of copies, page range, feeder choice, etc.). This may not be what you intended.
FUNCTION PrPSFileDialog(thePrRecHdl: lwTHPrint): BOOLEAN;
VAR
ourReply: StandardFileReply;
tempFlags: INTEGER;
tempResult: BOOLEAN;
ourOSError: OSErr;
tempPtr: Ptr;
BEGIN
PrPSFileDialog := FALSE; { being conservative }
tempResult := PrValidate(THPrint(thePrRecHdl));
IF NOT thePrRecHdl^^.prFlag1.fPSFile THEN
{ don't do this if there's already a file to be saved! }
thePrRecHdl^^.lastDirID := ourReply.sfFile.parID; {ID of
user-chosen directory}
PrPSFileDialog := TRUE;
END {IF good THEN…}
END {if flag was clear}
END;
PR 12 - SetLineWidth Revealed
Printing
Revised by: March 1988
Written by: Scott "ZZ" Zimmerman November 1987
This technical note describes the internal implementation, and correct method of using, the SetLineWidth Picture Comment.
The SetLineWidth picture comment provides a way of accessing PostScript’s 'setlinewidth' operator. Since the LaserWriter resolution is roughly four times that of the Macintosh screen, fractional line widths can be printed. The SetLineWidth PicComment provides a way for applications to access these fractional line widths through PostScript, without having to use floating point numbers.
First of all, the LaserWriter has an internal state that is stored in a number of PostScript variables. For more information on PostScript variables, see the PostScript Language Reference Manual. Some operations performed on the LaserWriter cause the values of these variables to change. One of these variables contains the width of the printer’s pen. The SetLineWidth picture comment works by changing the value of this variable.
Before we look at what the SetLineWidth comment does, let’s look at the argument passed to the comment. The argument is represented as a QuickDraw Point, however it is interpreted by the LaserWriter as a fraction. The LaserWriter interprets a point(h,v) to be a real number whose value is (v / h). This means that a point whose value is h=2, v=1, will be converted to 0.5 before being used by the LaserWriter. If you wanted to pass a value of 0.25, you would pass a point whose value is h=4, v=1. For 1.25, pass a point, h=4, v= 5.
In addition to the pen width variable, there is a variable that is used for scaling the pen’s width. This variable, named pnm for PeN Multiplier, contains a real number which is applied to the pen width. The default value of pnm is 1.0, which causes no scaling of the line width.
Whenever the SetLineWidth PicComment is sent to the LaserWriter, the current value of pnm is replaced by the value passed to the PicComment. The current pen size is then scaled by the new value of pnm. The following example will display four lines of different sizes. It is meant to illustrate the interaction between the QuickDraw PenSize procedure and the SetLineWidth PicComment.
TYPE
widthHdl = ^widthPtr;
widthPtr = ^widthPt;
widthPt = Point;
VAR
theWidth: widthHdl;
BEGIN
(* Initialize the print manager as per Inside Macintosh II-155. *)
At this point, it is assumed that PrPageOpen has been called, and the print manager is ready to accept data.
The first thing we do is set the scaling factor to 1.0. This way, no scaling will be performed when we call PenSize.
DrawString('5.0 times 1 point pen size = 5 point thickness.');
If any calls to PenSize are made at this point, the new pen size will be scaled by 5.0. This is because the SetLineWidth PicComment is still in effect. We will now send a SetLineWidth PicComment to revert the scaling factor back to 1.0.
DrawString('0.2 times 5 point pen size = 1 point thickness.');
Since the scaling is once again 1.0, PenSize calls at this point will not be scaled. Here we explicitly set the scaling factor to 1.0 before changing the pen size. This makes it easier to see what scaling will be applied to the next call to PenSize.
DrawString('1.0 times 1 point pen size = 1 point thickness');
(* Dispose of the handle when you are through with it! *)
DisposHandle(Handle(theWidth));
When printed, the above example will produce the following:
To summarize, there are four things to remember when using the SetLineWidth PicComment:
1. The argument to the SetLineWidth PicComment is specified as a point, though it is actually interpreted by the LaserWriter as a real number. The point value is specified as h,v, and the LaserWriter interprets the value as v / h.
2. The SetLineWidth PicComment affects both the height and width of the pen, even though the name suggests otherwise.
3. When you send the SetLineWidth PicComment, the current pen size will be scaled. Any drawing that is done after the PicComment is set, will be done with the scaled pen size.
4. When you call the QuickDraw PenSize procedure, the pen size will be scaled after it has been set. For example, if your scaling factor is 0.5, and you set the pen size to 2,2, the actual pen size will be 1,1. If you don’t want the scaling to occur, make sure to send a SetLineWidth PicComment, with the point argument set to 1,1. The next call to PenSize will then be scaled by 1.0, which will have no effect.
Further Reference:
• LaserWriter Reference Manual
• PostScript Language Reference Manual
• PostScript Language Tutorial and Cookbook
PR 13 - Spooler Queries?
Printing
Revised by: March 1988
Written by: Ginger Jernigan July 1987
When the LaserShare spooler is on an AppleTalk network, it acts like a LaserWriter-type device, which can be chosen and communicated with much like a real LaserWriter. Some applications, however, must communicate with a LaserWriter directly, not a spooler. If this is true for your application, you can check whether you are actually talking to a real LaserWriter by sending to the LaserWriter the following query:
%!PS-Adobe-1.2 Query
%%Title: Query to Spooler/Non-Spooler status
%%?BeginSpoolerQuery
(0) = flush
%%?EndSpoolerQuery 1
%%EOF
(The query has to be sent using the Printer Access Protocol (PAP). The object code for PAP is available from Licensing.) If the string returned begins with a ‘%%’ then it is a status string and you can ignore it and wait for another string. If the LaserWriter is actually a LaserShare spooler, then the string that is returned will be ‘1’. If the LaserWriter is a real LaserWriter then the string returned will be ‘0’.
Further Reference:
• PostScript Language Reference Manual
• Adobe Systems Document Structuring Conventions
PR 14 - Dictionary Downloading
Printing
Written by: Zz Zimmerman April 1991
This technical note discusses a method for downloading PostScript dictionaries automatically using the LaserWriter driver. It will also provide the format and use of the PREC(103) resource. It will also describe some problems with the now obsolete PREC(201) resource. If you are using PostScript dictionaries, or either of these resources, you should definitely read this note.
Introduction
Although many picture comments have been added to support the features of PostScript that are missing from Quickdraw, many developers have still taken to sending PostScript directly from their applications. As the use and complexity of this PostScript code increases, more and more developers are finding it necessary to define and use their own PostScript dictionaries. PostScript dictionaries are basically collections of variables and procedures that can be predefined, and accessed later. They are used to prevent conflicts between the symbols used by applications and those used by system software (such as the LaserWriter driver's LaserPrep dictionary). Unfortunately, because of the LaserWriter drivers habit of using the PostScript 'save' and 'restore' operators, there are problems with keeping a PostScript dictionary defined. PostScript definitions made by code sent with the print job (ie. sent between the calls to PrOpenPage/PrClosePage) will be lost the first time the LaserWriter driver calls 'restore'. There are a couple of solutions to this problem, but one that hasn't been documented before involves the use of the PREC(103) resource. If the LaserWriter driver finds a resource of type PREC with an ID of 103, it will download the PostScript code to the LaserWriter before performing the initial 'save' operation. This means that any definitions made by the PostScript code stored in the PREC(103) resource will remain defined for the duration of the print job, independent of the LaserWriter driver's calls to save and restore.
Caveats
The PREC(103) method is a great way to get a dictionary downloaded at print time. Unfortunately, this does not solve the problem for using dictionaries in export files like PICT. If you insert PostScript code into Quickdraw pictures, the system is not smart enough to record the PREC(103) code into the picture. Instead, you must record the dictionary using the standard PostScript picture comments (defined in Technical Note #91, Optimizing for the LaserWriter–PicComments). You should also use the appropriate PostScript structuring comments as defined by the Adobe Document Structuring Conventions. If you use the Adobe comments correctly, an application that is importing your picture will have the option of parsing for the procedure set comments, and extracting the dictionary definition to be placed in a PREC(103) resource at print time.
The next caveat concerns the use of multiple PREC(103) resources. At PrOpenDoc time, the LaserWriter driver makes one GetResource call to load the resource of type PREC with an ID of 103. Because the call is a GetResource call (instead of Get1Resource), the PREC can be stored in any open resource file. To avoid any conflicts, the resource should be stored in the resource fork of your application, or in the document file that is referencing the PostScript dictionary. Because the GetResource call is only made once, only the first PREC resource found by GetResource will be used. Any other PREC(103) resources will be ignored. As long is this resource is only used by applications, there is no problem since there can only be one application active at any particular time. If the resource is used by other elements of the system (ie. desk accessories, drivers, INITs), you can easily run into the problem of your PREC resource being ignored. The best solution to this problem is to only use the resource from within an application.
Since the PREC(103) resource is considered part of the print job, the definitions it makes are lost when the job ends (ie. when the LaserWriter receives EndOfFile from the Macintosh). Because of this, the code you place in the PREC(103) resource should not attempt changing any persistent parameters in the printer. The means avoiding the PostScript 'exitserver' operator. You should also avoid calling other routines that reset the current state of the printer (ie. initclip, initgraphics, etc.). Use of these operators will have a serious effect on Quickdraw operations that may be present in the print job.
When the PREC(103) resource was originally introduced, it had a cousin called PREC(201). PREC(201) was similar to the PREC(103) resource in that it allowed PostScript to be downloaded to the printer before the actual print job. The difference between the two resources was that the PREC(201) resource downloaded the PostScript code at the same time that it downloaded the LaserPrep dictionary, outside of the PostScript 'server loop'. Because of this, any definitions made by the code in the PREC(201) resource would remain after the current job. Like the LaserPrep dictionary, the dictionary downloaded in PREC(201) would remain until the LaserWriter was rebooted (ie. powered off then on again). Although this feature was useful in some situations, it did have its problems. Not the least of which was the valuable printer memory consumed by the dictionary that was downloaded. Since the dictionary remained after the job that required it, subsequent jobs had less memory available to them. The only way to reclaim the memory was to reboot the printer, and this was not obvious to naive users. The other problem was introduced when background printing became available. With background printing enabled, the LaserWriter driver could no longer count on the PREC(201) resource always being available. Since you could no longer store the resource in the LaserWriter driver (because of the LaserWriter driver being MultiFinder compatible - see Learning To Drive for more information), it has to be stored in a separate resource file. This made it virtually impossible for the LaserWriter driver to find the resource when it was required. For this reason, the PREC(201) resource is only downloaded when background printing is turned off.
Needless to say, we don't recommend the use of features that only work in certain situations, so the PREC(201) resource is now considered unsupported and obsolete. If you are using the PREC(201) resource, you should be able to revise your application to use the PREC(103) resource instead, with only a small performance penalty. On the bright side, the PREC(201) resource will continue to be supported in the foreground through the 7.0 version of the LaserWriter driver, and most likely, until the new printing architecture becomes available, giving you plenty of time to revise...
Implementation
The PREC(103) resource can be implemented by simply creating the resource with ResEdit or Rez, and then storing it in an open resource file at print time. In the case of ResEdit, you should create a new resource of type PREC with an ID of 103. You should then open the new resource using the resource template for string ('STR ') resources. You can then type your PostScript code directly into the resource.
If you would rather keep your PREC definition as a Rez source file with the rest of your project, you can do this by simply defining the PREC resource type at the top of the file, followed by an instance of the PREC resource. Consider the following Rez source code:
/* First the resource type definition: */
type 'PREC' {
string;
};
/* Now the real resource definition: */
resource 'PREC' (103) {
"userdict /mydict 50 dict def";
};
We begin by defining the resource type as being a string. We then define an instance of the resource with an ID of 103. Finally, we define the contents of the resource. The PostScript code above basically defines a dictionary named mydict within the userdict dictionary. The mydict dictionary is defined as having a maximum of 50 elements. Consult the PostScript Language Reference Manual for more information concerning legal operations on dictionaries.
Conclusion
The PREC(103) is a simple, efficient way to download a PostScript dictionary at print time. It does not solve the problem of exporting PostScript that references a dictionary into file formats such as PICT, but it can help. Applications can be revised to extract PostScript dictionary definitions from files such as PICT and download them at print time using the PREC(103). It should be noted however that this is not automatic, the application must parse the picture to get this functionality. Finally, the PREC(201) resource can only be supported when background printing is disabled, so it is now considered obsolete, and use of it is unsupported.
Further Reference:
• PostScript Language Reference Manual, Adobe Systems Inc.
• Adobe Document Structuring Conventions, Adobe Systems Inc.
• LaserWriter Reference Manual, Addison-Wesley
PostScript is a registered trademark of Adobe Systems Incorporated.
PR 15 - Feeder Fodder
Printing
Written by: Zz Zimmerman April 1991
This Technical Note discusses the new Feeder button available in the 6.1, and 7.0 versions of the LaserWriter driver. This Feeder button mechanism allows developers to insert code into the LaserWriter driver to support a sheet feeder connected to a LaserWriter. This Note provides a description of the button, as well as information required to implement one.
Introduction
The LaserWriter driver now implements a standard method for handling sheet feeders. Most LaserWriter sheet feeders need a way to present a user with a dialog as well as a way to download the PostScript® code necessary to control the feeder. In the past, most manufacturers resorted to modifying the LaserWriter driver’s code resources; however, this functionality is now possible without the need to patch existing resources in the driver—by adding three new resources.
When the LaserWriter driver notices these three special resources in its resource fork, it displays a Feeder button in the lower right corner of the Print dialog box. It is important to note that this feature is not provided for general application use, but rather only for developers of sheet feeders and other LaserWriter add-on devices. The button is always labeled Feeder, and there can be only one set of Feeder resources in the LaserWriter driver. Because of this restriction, you should not attempt to use this feature to implement anything other than a sheet feeder.
The first special resource contains code to implement the user interface of the feeder, and the other two contain the PostScript code required to drive the feeder. When an application calls the LaserWriter driver to display the Print dialog box, the driver looks for three resources of type 'feed' and displays the Feeder button in the lower right corner of the dialog box if they are found. If no 'feed' resources are available, it does not display the Feeder button.
When a user selects Print, the driver displays the standard Print dialog box with the Feeder button. If a user clicks on the Feeder button, the driver displays a dialog box in front of the Print dialog box, which allows the user to configure the feeder, then returns to the Print dialog box once the user confirms or cancels the feeder configuration. This feeder dialog box should not contain an option to print, as this could override choices made in the standard Print dialog box.
Implementation
To handle interaction with the user, you must install a resource of type 'feed' (ID = -8192) into the LaserWriter driver with the code required to manage the dialog box. Like all Printing Manager code resources, this resource begins with a jump table, followed by the actual code. The code is implemented as a procedure that is passed a single parameter. This parameter is a rectangle defining the page size selected by the user. This page size is equivalent to the rPaper rectangle in the print record, meaning it defines the actual page size, not just the printable area. The rectangle is expressed in 72 dpi coordinates and has a negative origin.
Go Ahead and Jump
The jump table consists of a 68000 JMP instruction that jumps to the proper offset in the resource. In this case, there is only one routine, so the code starts immediately following the jump table. To make this step automatic, the jump table is created using a small assembly language header:
IMPORT Feeder ; Feeder is NOT defined here...
Start MAIN EXPORT ; This is the main entry point for the linker.
JMP Feeder ; The one jump table entry in this table.
END
This example first imports the Feeder procedure, which can be defined externally in the language of your choice. Next is Start, the main entry point to the jump table. By passing this label to the link command, the jump table is located at the beginning of the resource. The next line is the actual jump table entry, and the END is required to end the assembly-language header. That’s all there is to it. The only thing one should have to change in this code fragment would be the name of the routine to import.
The Real MacCode
Now that the jump table is complete, it needs some place to jump. Although MPW C and Pascal examples are provided in this Note, the code can be written in any language. As mentioned before, the code is implemented as a procedure that takes one parameter.
C Definition
In C, this looks like:
#include <Types.h>
#include <Quickdraw.h>
void FEEDER(Rect *r)
{
<Code to present and handle dialog...>
}
Since the assembler converts all labels to uppercase, the name of the procedure FEEDER must be capitalized to match the case of the label in the jump table. If you are using MPW, you can use the assembler's CASE directive to prevent the assembler from capitalizing the labels. Since the rectangle is passed using the C calling convention (i.e., the caller strips the parameter), there is no need to declare the procedure as type Pascal. However, this convention does make things a little more interesting for the Pascal version:
Pascal Definition
If you are using MPW, you can use the Pascal compiler's C directive to define the Feeder procedure as using the C calling convention. This makes the definition look like this:
UNIT FeederSample;
INTERFACE
USES Types, Quickdraw;
PROCEDURE Feeder(r: Rect); C;
IMPLEMENTATION
PROCEDURE Feeder(r: Rect);
BEGIN
<Code to present and handle dialog...>
END;
END.
So this is straight forward. The procedure Feeder is defined as having one parameter (r), and the C directive is used so that the stack is handled correctly.
If you are using some other development environment that doesn't support the C directive, you have to do a little more work, making the definition look like this:
UNIT FeederSample;
INTERFACE
USES Types, Quickdraw;
PROCEDURE Feeder;
IMPLEMENTATION
FUNCTION StealRectalParam: Rect;
INLINE $2EAE, 0008; { MOVE.L 8(A6),(A7) }
PROCEDURE Feeder;
VAR
r: Rect;
BEGIN
r := StealRectalParam;
<Code to present and handle dialog...>
END;
END.
First of all, a unit is defined, and the proper interfaces are included. The definition of the Feeder procedure in the INTERFACE section is required to make the label available to external modules. In the IMPLEMENTATION section, one starts with the StealRectalParam function, which is used to get the rectangle passed by the Printing Manager without actually removing it from the stack. If you declared the rectangle as a parameter to the Feeder procedure, Feeder would remove the parameter before returning, then when the caller tried to remove the parameter again, the stack would be invalid and would cause a crash.
To solve this problem, define the Feeder procedure with no parameters. This way, the Feeder procedure leaves the parameter right where the caller left it. To get the parameter without removing it from the stack, use the StealRectalParam function, which moves the parameter from its normal location (off of A6) into the location pointed to by the stack pointer. Since StealRectalParam is a function, the stack pointer is already pointing to the return value. When StealRectalParam returns, the Feeder routine gets the rectangle parameter, without having removed it from the stack.
Tickled Link
Now you have the jump table and the code, but you still need to link them together. This step is pretty simple, but remember to specify the starting location of the jump table. It looks like the following:
First tell the linker to link the code into a 'feed' resource with an ID of -8192. Next, specify that the resource begins with the code at label START. This label was defined by the assembly-language used to generate the jump table. Finally, tell the linker to link all of the code into a single segment named Feeder. Obviously, the list of libraries and object files changes depending upon the language used, but the directives to the Link command should remain the same.
Well Fed
So that should be enough to get some code into the 'feed' resource. Now you need to actually control the feeder during the print job. To do this, you must use PostScript. Your driver should also provide a 'feed' resource of -8191 containing PostScript code. This code is downloaded by the LaserWriter driver prior to downloading the rest of the job. For those familiar with the 'PREC' 103 resource, the PostScript in the 'feed' resource is downloaded before the 'PREC' 103 code. Additional PostScript to be downloaded can be stored in 'feed' -8190. The PostScript code in the 'feed' resource should redefine (i.e., patch) the PostScript operators required to handle switching feeders. A likely candidate is the showpage operator called at the end of each page. As always, calling or redefining operators defined by the LaserPrep (md) dictionary is not supported. If your device is connected via the LaserWriter’s serial port, you can license code from Adobe Systems, Inc. that makes it possible to access the serial port while the LaserWriter is connected over AppleTalk. For more information, contact Adobe at:
Adobe Systems, Inc.
1585 Charleston Road
Mountain View, CA 94043
(415) 961-4400
Once a user has confirmed the configuration from the dialog box, you can edit the PostScript code in the -8191 resource to reflect the choices made. However, when MultiFinder is active, you cannot add or change the size of resources in the LaserWriter driver. For this reason, you should pad the 'feed' -8191 resource to the maximum size. This padding can be done by adding spaces at the end. If you later need to resize the resource, you can simply overwrite some of the spaces. For more information on printer drivers under MultiFinder, see the Learning to Drive document, which is part of “Developer Essentials,” and is available on AppleLink, the Apple FTP site, and the Developer CD Series.
You probably need to provide other resources along with the 'feed' resources; for example, you need 'DITL' and 'DLOG' resources for the dialog box. This is okay, but you should be sure to pick unique resource types to avoid confusing the LaserWriter driver. In the case of a Feeder button, you are a guest in someone else’s house. It would be wise to avoid rearranging the furniture.
When the LaserWriter driver actually opens the connection to the printer, it looks for 'feed' resources -8191 and -8190. If they exist, they are downloaded. For those familiar with the 'PREC' 103 method of downloading PostScript code (refer to Technical Note #192, Surprised in LaserWriter 5.2 and Newer), the 'feed' resources are downloaded before the 'PREC' 103 resource. In the case of background printing, the resources are copied into the spool file. Since 'feed' resources -8191 and -8190 are automatically downloaded by the LaserWriter, they must contain PostScript code. The format of these PostScript resources is a string of ASCII characters without any length byte or terminator. The size of the string is determined by the size of the resource; there are no special size restrictions on these resources, and their only requirement is that they contain PostScript code. To make debugging easier, you should separate lines of PostScript using a carriage return character (13 or $0D hex).
Don't Feed The Print Monster
One last important note concerns the 6.1 version of the LaserWriter driver, shipped on the Macintosh Printing Tools disk included with the Personal LaserWriter LS and StyleWriter. In this version of the driver, the Feeder button will only work when Background printing is disabled. There is a problem with the driver finding the 'feed' resources when Background printing is enabled. This problem has been solved in the 7.0 version of the driver which should be used instead of the 6.1 driver as soon as it is available. Since there is no workaround for the problem, you don't really have to do anything except for possibly noting it in your documentation. Any note should recommend upgrading to the 7.0 version of the driver as soon as possible.
Driving Miss Lasey
Now that you have the two or three 'feed' resources, the big question is installation. How should you ship these things? There are two methods. The first method involves licensing the LaserWriter driver from Apple Software Licensing (SW.License on AppleLink). This method is only required for “turn-key” systems, where all installation is done for the user and you must ship the LaserWriter driver as part of your product. The second method, which is by and large preferred as it requires no licensing, is to ship your resources in an installer application. This application simply opens the LaserWriter file and adds the necessary resources.
Conclusion
So this should be all the information you need to implement the feed resources for your device. If you intend to drive a sheet feeder through the LaserWriter’s serial port, be sure to contact Adobe Systems, Inc. for the most current implementation and licensing information. Although the Feeder button could theoretically be used for other purposes, it will always be labeled “Feeder” by the LaserWriter driver. Because of this consistency, developers should not attempt to extend its functionality beyond support for sheet feeders.
Further Reference:
• PostScript Language Reference Manual, Adobe Systems Inc.
• Technical Note M.IM.LWDriverSuprises —
Surprises in LaserWriter 5.2 and Newer
PostScript is a registered trademark of Adobe Systems Incorporated.
PR 16 - Fun With PrJobMerge
Printing
Revised by: Matt Deatherage May 1992
Written by: Scott “Zz” Zimmerman and Matt Deatherage March 1992
This Technical Note discusses some interesting behavior you’ll encounter while using PrJobMerge with the 7.0 and 7.1 versions of the LaserWriter driver.
Changes since March 1992: Corrected the Vulcan-like “THPring” typo to correctly read “THPrint,” and changed a comment in the code to mean what I originally meant.
Like many Printing Manager calls, PrJobMerge is implemented by the currently chosen printer driver. This makes sense after consideration—since the printer driver may store job-specific information anywhere in the print record, only the printer driver can correctly merge this into a destination print record.
The LaserWriter driver’s implementation of PrJobMerge has a few bugs in versions 7.0 and 7.1.
Fun Thing #1
Historically, PrJobMerge hasn’t worked correctly in the LaserWriter driver. The driver does not correctly merge all job-related data (like the number of copies requested) into the destination print record, so printing multiple copies of multiple documents from the Finder isn’t really possible with the LaserWriter driver.
The only possible workaround is to present a different job dialog for each document to be printed, but this isn’t recommended—especially since the job dialog doesn’t tell you which document you’re about to print.
Fun Thing #2
As if this wasn’t enough excitement for one driver, in versions 7.0 and 7.1 of the LaserWriter driver PrJobMerge actually manages to destroy all the job-specific information in the source print record after it doesn’t copy it into the destination print record.
There is a workaround for this problem—make a copy of the source print record and pass the copy to PrJobMerge. If you pass the copy to PrJobMerge, you can just replace PrJobMerge with your own routine that makes a copy, merges it into the destination, and disposes of the copy. This will work for all printer drivers, although it’s necessary only with version 7.0 of the LaserWriter driver.
hPrintTemp := hPrintSrc; {make our own copy of the print record handle}
copyError := HandToHand(Handle(hPrintTemp));
PrSetError(copyError); {so we can get it later}
IF copyError = noErr THEN BEGIN
{hPrintTemp is now a copy of the original source record}
PrJobMerge(hPrintTemp,hPrintDst); {This messes up hPrintTemp, but we
don't care}
END; {if copyError = noErr}
IF hPrintTemp <> NIL THEN DisposHandle(Handle(hPrintTemp)); {only a copy,
remember!}
END;
Don’t Go Overboard Trying to Solve This
Although the bugs in PrJobMerge in versions 7.0 and 7.1 of the LaserWriter driver make certain kinds of printing multiple documents impossible without device-specific workarounds, we strongly encourage you not to implement such code. Any code that tries to replicate the function of PrJobMerge must by nature depend on how the LaserWriter driver stores information in the print record, and this is a Bad Thing. The road to Compatibility Hell is paved with good intentions.
If you write your code as described in this Note, it will behave properly when the bug is fixed without change on your part. If you go overboard trying to write your own PrJobMerge function, your application is a prime candidate for compatibility problems.
Further Reference:
• Inside Macintosh, Volume II, The Printing Manager
PR 17 - ImageWriter II Paper Motion
Printing
Revised by: March 1988
Written by: Ginger Jernigan April 1986
The purpose of this technical note is to answer the many questions asked about why the paper moves the way it does on the ImageWriter II.
Many people have asked why the paper is rolled backward at the beginning of a Macintosh print job on the ImageWriter II. First, note that this only happens with pin-feed paper (i.e. not with hand-feed or the sheet-feeder) and only at the beginning of a job.
It is not a bug, and it is not malicious programming. It is simply that users are told in the manual to load pin-feed paper with the top edge at the pinch-rollers, making it easy to rip off the printed page(s) without wrecking the paper that is still in the printer or having to roll the paper up and down manually. At the end of every job, the software makes sure that the paper is left in this position, leaving the print-head roughly an inch from the edge. If something is to be printed higher than that, the paper has to be rolled backwards.
As you are probably aware, the “printable rectangle” (rPage) reported to the application by the print code begins 1/2 inch from the top edge, not one inch. The reason for that is that we want a document to print exactly the same way whether you are printing on the ImageWriter I or II. On the ImageWriter I, the paper starts with the print-head 1/2 inch from the top edge, so the top of rPage is at that position for both printers.
There is no way to eliminate the reverse-feed action, because the user would have to load the paper a different way AND the software would have to know that this was done.
Incidentally, in addition to the paper motion described above, there is also the “burp.” This is a 1/8-inch motion back and forth to take up the slop in the printer’s gear-train. It is needed on the old-model printer, and there is debate about whether or not it’s needed on ALL ImageWriter IIs, or only some, or none. The burp has been in and out of the ImageWriter II code in various releases; right now it’s in.
PR 18 - The Effect of Spool-a-page/Print-a-page on Shared Printers
Printing
Revised by: March 1988
Written by: Ginger Jenigan May 1987
This technical note discusses drawbacks of using the spool-a-page/ print-a-page method of printing.
The “spool-a-page/print-a-page” method of printing prints each page of a document as a separate job instead of calling PrPicFile to print the entire picture file. Many applications adopted this method of printing to avoid running out of disk space while the ImageWriter driver was spooling the document to disk. As long as you are printing to a directly connected ImageWriter, you’re fine, but if you are printing to remote or shared devices (like the AppleTalk ImageWriter and the LaserWriter), this method may create significant problems for the user.
When a job is initiated by the application, the driver establishes a connection with the printer via AppleTalk. When the job is completed, the driver closes the connection, allowing another job the opportunity to print. If each page is a job in itself, then the connection is closed and reopened between each page, allowing another application to print between the pages of the document, which, as you might imagine, could present a significant problem. If two people are printing to the same AppleTalk ImageWriter at the same time and their applications use the “spool-a-page/print-a-page” method of printing, the pages of each document will be interleaved at the printer.
Although there are good reasons for using this method of printing, it is only useful for a directly connected printer. From a compatibility point of view, this method of printing is built-in device dependence. Also, this method could create serious problems for other types of remote devices. Therefore, we are recommending that applications avoid using this method indiscriminately. You should check available disk space to see how much room you have before you print. If there isn’t enough space for your entire document, then print as much as you can (to minimize the interleaving) before starting another job. Whenever possible, applications should use the print loop described on page II-155 in The Printing Manager chapter of Inside Macintosh.
Further Reference:
• Printing Manager
• Technical Note M.IM.LaserWriterOpt —
Optimizing for the LaserWriter—Techniques
PR 19 - Using Laser Prep Routines
Printing
Revised by: March 1988
Written by: Ginger Jernigan July 1987
This technical note addresses the issues involved in depending on the procedures and constants defined in the Laser Prep dictionary.
When you are writing an application that uses PostScript heavily, it is very tempting to call the procedures already defined in the Laser Prep dictionary, rather than taking up the space in the printer’s memory with PostScript procedures of your own. Or, if a procedure in the dictionary doesn’t do what you need it to do, it is tempting to go in and change it to do what you really want.
Unfortunately, we cannot guarantee that either the name or the function of a particular procedure in the dictionary will stay the same when the LaserWriter driver changes. In addition, some procedures may become obsolete and go away when the driver changes. Since the Laser Prep dictionary is considered part of the source for the LaserWriter driver, Apple reserves the right to make any changes to it that are deemed necessary.
Because we cannot guarantee the permanence of the contents of the Laser Prep dictionary, relying on its contents can pose a significant compatibility problem. If you rely on the procedures defined in the Laser Prep dictionary, your application will have to be revised every time Apple releases a new LaserWriter driver.
If you feel that you absolutely must use or modify procedures in the Laser Prep dictionary, you must always check the version that is loaded into the printer before you print. This allows your application to take appropriate action if the version of the dictionary that has been downloaded to the printer isn’t one that you know about.
How To Check The Laser Prep Dictionary Version
To determine the version of Laser Prep that the printer may contain, you have to communicate with the printer using the Printer Access Protocol (PAP); you can’t just send your query through the LaserWriter driver because there is no way to get an answer back. The object code and documentation for PAP are available from Apple’s Licensing department.
To determine whether the dictionary has been downloaded and whether it is the right one, send the following PostScript code to the printer:
%!PS-Adobe-1.2 Query
%%Title: Query to establish Laser Prep ProcSet version propriety
%%?BeginProcSetQuery: AppleDict md xx
/md where{
/md get /av get cvi xx eq
{(1)}{(2)}ifelse}
{(0)}ifelse = flush
%%?EndProcSetQuery: unknown
%%EOF
md is the name of the Apple dictionary and xx is the version number you want.
Note: /av is a constant in the md dictionary which contains the dictionary’s version number. This is the only object in the dictionary whose name and function are guaranteed not to change.
From the printer you will receive a string. If the string returned begins with ‘%%’, it is a Status response. You can ignore it and wait for another string.
If the response is ‘0’, the dictionary hasn’t been downloaded yet; you will need to determine how to best handle this situation for your application.
If the response is ‘1’, the printer is loaded with the correct version of the dictionary.
If the response is ‘2’, then the dictionary exists but it isn’t the version you need. In this case you need to either let the user know, or proceed in as standard a fashion as possible, without calling or modifying routines contained in the Laser Prep dictionary.
Translating PostScript Files
Some applications interpret the PostScript files that are generated by the LaserWriter driver when the user presses command-F (generates document only) or command-K (generates dictionary and document) after clicking on the OK button in the Job dialog. A typical application might translate these PostScript files into another page description language.
This kind of application requires intimate knowledge of the contents of the dictionary in order to be able to do the translation, because it may have to expand the procedures used to their actual values before it can then translate the PostScript to another language. This poses a significant compatibility problem. Since we cannot guarantee that the contents of the dictionary will not change, these types of applications will have to be revised every time we release a new version of the LaserWriter driver. Also, there is no way to know which version of the LaserWriter driver generated the PostScript file the application is interpreting. You will have to require that a particular version of the LaserWriter driver be used to generate the PostScript files that your application will interpret.
Printer Independence
Applications that are written to take advantage of the routines in the Laser Prep dictionary are, of course, highly device dependent. Being device dependent can drastically reduce your chances of being compatible with future printer-type devices. For a more detailed discussion of this issue, please refer to Technical Note #122.
Further Reference:
• The Printing Manager
• PostScript Language Reference Manual
• Technical Note M.IM.DevIndPrinting —
Device-Independent Printing
PR 20 - LaserWriter Driver Surprises in 5.0 and Newer
Printing
Revised by: Mary Burke & Scott “Zz” Zimmerman February 1990
Written by: Scott “Zz” Zimmerman April 1988
This Technical Note describes some changes in version 5.0 and later LaserWriter drivers.
Changes since April 1988: Described a bug in 5.x which is fixed in 6.0 and later, and reiterated a warning about storing fonts in an application.
With the release of LaserWriter 5.0 and background printing, a few changes had to be made to the LaserWriter driver. Although these changes were transparent to most applications, some have had problems. Most of these problems are related to use of unsupported features. This Note details a partial list of the changes.
No Mo’ Low
Because of the problems supporting both the high-level and low-level interfaces in the background, the low-level interface is all but removed. Instead of the low-level calls being executed by the device driver, the _PrCtlCall procedure converts the call into its high-level equivalent before execution. This way, the LaserWriter driver has a common entry point for both the low-level and high-level interfaces. Because of this conversion, the low-level calls may not be faster than using the high-level equivalents. In some cases, they may even be slower.
Version 5.x of the LaserWriter driver also contains a bug with the low-level interface. If an application which uses the low-level Printing Manager interface encounters an error during the course of the print job, the LaserWriter driver crashes before the application has a chance to see the error. Because the error occurs inside the driver, there is no way for an application to predict or work around this problem. The only solution to this problem is to use the high-level Printing Manager interface or to upgrade to version 6.0 or later of the LaserWriter driver which fixes this bug.
Are You Convertible?
Whereas the conversion of the low-level calls should be transparent, the conversion routines make some assumptions. The conversion routines require a context in which to operate; the Printing Manager maintains a certain state while executing commands, and the conversion routines need access to this state to perform the conversion. To provide this context, an application must have opened a document and a page. This requirement means that the original method of using the low-level interface, which is documented in Inside Macintosh, Volume II-164, no longer works, as in the following example:
PrDrvrOpen;
PrCtlCall(iPrDevCtl, lPrReset, 0, 0);
{ Send data to be printed. }
PrCtlCall(iPrDevCtl, lPrPageEnd, 0, 0);
PrDrvrClose;
Instead, an application should use the following:
PrDrvrOpen;
PrCtlCall(iPrDevCtl, lPrDocOpen, 0, 0);
PrCtlCall(iPrDevCtl, lPrPageOpen, 0, 0);
{ Send data to be printed. }
PrCtlCall(iPrDevCtl, lPrPageClose, 0, 0);
PrCtlCall(iPrDevCtl, lPrDocClose, 0, 0);
PrDrvrClose;
This method provides the Printing Manager with the context it needs to convert the calls.
Really Unsupported Features
Sending data to the printer between the _PrOpenDoc or lPrDocOpen and the _PrOpenPage or lPrPageOpen calls is not currently, and has never been supported. LaserWriter drivers prior to 5.0 interpreted this data, but 5.0 and later drivers ignore it. To download an application-specific PostScript® dictionary as a header with each document, Apple recommends that the application provide a 'PREC' resource of ID = 103, as is described in the LaserWriter Reference.
A Little Less Control
Four of the six printer control calls originally supported by the LaserWriter driver have been discontinued due to lack of use and difficulty supporting with background printing. The four calls which follow were only supported by the LaserWriter driver and only documented in the LaserWriter Reference Manual:
°•° fill °•° hexBuf °•° printR °•° printF
In addition to these calls, the stdBuf call is also affected. There are two versions of the stdBuf call depending upon the sign of the bytes parameter. If bytes is negative, the text passed to the stdBuf call is converted to PostScript text before being sent to the LaserWriter. This conversion means that special PostScript characters in the text are preceded by a PostScript escape character. In addition, characters with an ASCII value greater than 128 are converted to octal before being sent to the LaserWriter. This version of the call is no longer supported.
If the bytes parameter is positive, the text passed to the call is sent directly to the LaserWriter without conversion and interpreted as PostScript instructions. This version of the call is still supported, but there is one more problem. When an application first opens the low-level driver (via _PrDrvrOpen) with background printing enabled, no clip region is defined. If the application then begins sending PostScript to the driver via the stdBuf call, all of the output is clipped, and only a blank page is printed.
To prevent this problem, the application must force a clip region to be sent to the LaserWriter. The region is sent by the driver when it receives its first drawing command. Unfortunately, the driver does not consider the stdBuf call to be a drawing command. To force the clip region on the printer, the application can use the iPrBitsCtl call to print a small bitmap outside the printable area of the page. This call does not have any effect on the document, but it fires the bottleneck routine and causes a definition of the clip region. Since the clip region is reinitialized at each call to lPrPageOpen, the application should send the bitmap once at the start of each page. If any other printer control calls precede the stdBuf call, the application does not need to send the bitmap.
Background Preparations
The 'PREC' ID = 201 mechanism only works when background printing is disabled. This limitation is because of difficulties finding the resource under MultiFinder. Since the option only works in the foreground, and since there is no way for an application to know if background printing is enabled, an application should avoid using this feature.
Fonts In An Application?
There are two problems when printing application fonts with the LaserWriter driver. Application fonts are fonts that are stored in the resource fork of the application’s resource file rather than being stored in the System file. The first problem occurs when the application font has the same name as the application’s resource file. If this is the true, the LaserWriter driver incorrectly assumes that the application’s resource file is a font file, and closes it after using the font. To solve this problem, developers should make sure the name of the application font (i.e., the 'FOND' resource) is different from the name of the application’s resource file. Since the application can still be renamed by the user, developers should try to select a unique font name. If the font name does not appear in a font menu, developers can simply append the application’s creator string onto the desired font name (i.e., 'MyApplicationFont ZZAP').
The second problem with application fonts only occurs when background printing is enabled. When a print job is performed in the background, the LaserWriter driver writes all of the data to be printed into a file (called a spool file) for printing at a later time by Print Monitor. Since the LaserWriter driver has no way of knowing when the file will actually be printed, it cannot assume that the application will still be open when the job is printed. This is a problem if the application contains application fonts that Print Monitor needs at print time. To solve this problem, the LaserWriter driver must determine whether the fonts needed by the application are resident in the System file or in the application file. If they are in the application file, the driver must copy them into the spool file so they are available to Print Monitor. This practice can lead to very large spool files, as well as a significant loss of performance when background printing is enabled. To solve these and other user interface problems related to application fonts, Apple strongly recommends that developers ship custom application fonts as suitcase files for the user to install in the System file.
Headin’ For Trouble
There is a minor bug in version 5.0 of the LaserWriter driver that only affects applications that parse the PostScript header downloaded by the driver with each document. This header contains some PostScript comments that provide information about the current job. One of these comments is IncludeProcSet. This comment takes three arguments: a PostScript dictionary name, a major version number, and a minor version number. In version 4.0 of the LaserWriter driver, the comment line looked like the following:
%% IncludeProcSet: (Appledict md) 65 0
Unfortunately, in version 5.0 of the LaserWriter driver, the last argument was removed. This caused the comment line to look like the following:
%% IncludeProcSet (Appledict md) 66
Since Adobe defined the comment to take three arguments, it is reasonable for applications that parse the comments to expect three arguments; therefore, version 5.1 and later of the LaserWriter driver contain the correct version of the comment:
%% IncludeProcSet (Appledict md) 67 0
No Go With Zero
Some applications want to force a font to be downloaded to the LaserWriter without actually printing characters with the font. This can be done in three easy steps:
1. Save the current pen position.
2. Use any text drawing routine to draw a space character.
3. Move the pen back to the saved position.
Some applications use _DrawString with a empty string (e.g., DrawString('')) to force the font downloading. Although this worked in LaserWriter drivers up to 5.0, these calls are ignored by the 5.1 and later drivers. The main reasons for this change were optimization of performance and a reduction in the size of spool files.
Further Reference:
• Inside Macintosh, Volumes II & V, The Printing Manager
• LaserWriter Reference Manual
PostScript is a registered trademark of Adobe Systems Incorporated.
PR 21 - Pictures and the Printing Manager
Printing
Written by: Zz Zimmerman April 1991
This technical note described some problems and features of using Quickdraw pictures with the Printing Manager. In general, if your application prints Quickdraw pictures, you should read this note.
Introduction
Most applications support Quickdraw pictures to some degree. They will allow you to import or export picture files, as well as using the PICT resource format on the clipboard to support Cut & Paste with other applications. Unfortunately, there are some problems that occur with pictures at print time, and that's what I want to cover here.
You PICT When?
One of the problems that comes up at print time is the use of picture comments. Some applications store their data in a native format, and only create pictures at print time to enable the use of picture comments. For each page of the document, they open a new picture, record the Quickdraw calls that described the document, along with any picture comments they want to use, and finally close the picture. When this is done, they call DrawPicture to print the picture, and then start the whole process over again for the next page.
This method is supported and fully compatible with future system software, but is not required. The Printing Manager spools each page of a document into a Quickdraw picture. Since the Printing Manager already has a picture open, it is totally legal to send a picture comment (via the PicComment call) in between calls to PrOpenPage and PrClosePage without having them recorded in a picture. The Printing Manager has already replaced the StdComment procedure with its own anyway, so the PicComment call will be intercepted and supported correctly by the Printing Manager. If the only reason you are recording into pictures is so that you can use PicComments, you can avoid the overhead at print time by simply sending the comments directly.
Feeling PICT On?
Even if you aren't sending picture comments, you may still need to create a picture at print time. In general, you should try to create any pictures you need prior to calling PrOpen. This is because there is no way to predict how much memory a particular printer driver will require. Instead, you need to make as much memory available as possible. If you are creating pictures with the Printing Manager open, the chances are good that you are using memory you can't afford to waste.
If you need to create a picture with the Printing Manager open, and memory is not a problem, you should still be aware of some potential problems. First of all, keep in mind the Printing Manager receives data from the application by replacing the Quickdraw GrafProcs stored in the GrafPort returned by PrOpenDoc. One of these procedures is the StdComment procedure which is called each time the application calls the Quickdraw PicComment routine. Since the Printing Manager has these routines patched, creating a picture in the Printing Manager's GrafPort can cause problems. If you must create a picture between calls to PrOpenPage/PrClosePage, you should be sure to set the port to a standard Quickdraw GrafPort before calling OpenPicture. Any GrafPort that was created by Quickdraw (as opposed to the Printing Manager) will work fine.
If you do create a picture at print time, you may experience a syndrome we call 'floating picture comments'. That is, calls made by your application to the PicComment routine will be recorded in a different order than you made them. This will usually cause them to effect the wrong part of the picture, and lead to endless confusion. The best solution to this problem is to create any pictures that your application will need before opening the Printing Manager.
Scaling Pictures - Mountains from Mole Hills
Another problem is a basic problem with pictures that seems to show up more at print time. The problem concerns the scaling of pictures using the destination rectangle passed to DrawPicture. This method will work for most pictures, but problems arise with more complex pictures, and for pictures that contain text. The problem is the method that Quickdraw uses to scale the text stored within pictures. When scaling, Quickdraw tries to handle the text scaling intelligently by changing the size of the font being used, as opposed to just scaling the bits. Unfortunately, the widths used by bitmapped fonts are not always linear (ie. the 12 point width isn't exactly 1/2 of the 24 point width). Because of this, you can run into problems with lines of text getting slightly longer or shorter as the picture is scaled. In many cases, the error is insignificant, but if you are trying to draw a line of text that fits exactly into a box (a spreadsheet cell for example), you might be surprised to see the line of text extending beyond the box when the picture is scaled.
There can also be problems when using certain picture comments or imbedded PostScript. In the case of the TextCenter picture comment, you specify an offset to the center of rotation. This offset is usually based on the metrics of the font being used. If you scale the picture, Quickdraw decides to use a different font, and the offset you originally specified will be incorrect.
The easiest way to solve these problems is to scale the picture yourself. Especially if you are trying to scale by a large amount. For example, some applications create a picture at 72 dpi (ie. dots per inch), and then scale it to 288 dpi for printing by simply increasing the destination rectangle by 4x. This is asking a lot of the system, and will result in the text problems described above. Instead, you should either draw the picture into its original frame, and let the Printing Manager handle scaling it to the resolution of the device, or handle the scaling yourself by parsing the picture and playing it back opcode by opcode instead of calling DrawPicture.
One last thing to watch for when scaling pictures is integer overflows. It's usually pretty rare that you will overflow a coordinate when creating a Quickdraw picture, but it is not so hard to do when scaling a picture. For example, some applications will draw something offscreen to make sure the Printing Manager has configured the clip region and other related structures. They usually do this by moving the cursor to (-32767,-32767), and then draw a pixel. This works fine to initialize the Printing Manager, and the pixel isn't actually seen on the output. The problem occurs when you try to scale this picture. If you try to make it bigger, Quickdraw will adjust to coordinate (-32767,-32767) which will end up overflowing. The only way to solve these problems is to look for these kinds of operations in the picture before trying to scale it with DrawPicture.
Pictures Within Pictures–Is Nesting the Best Thing?
One cool feature of Quickdraw pictures is the ability to nest them easily. Basically, you can call OpenPicture, and then call DrawPicture with multiple pictures, and when you call ClosePicture, they will all have been recorded into one picture. Very cool. The problem comes when you start using the Begin/End form of picture comments. There are some comments like PostScriptBegin/PostScriptEnd, and TextBegin/TextEnd that have a begin comment that is followed by an end comment. When using these comments, it is very important to make sure that you have an end for each begin that you have sent. If the nesting gets off, you will, at the least, get incorrect output, though it is more likely that the Printing Manager will actually crash. If your application is generating picture comments, it is very simple to make sure that you have an end for each begin. But when nesting a picture that you have imported from another application, it is important to know how its comments will interact with yours.
In most cases, you can simply call DrawPicture to render the picture to the Printing Manager and you don't have to worry. But if you are creating a picture for export, you may have to nest multiple pictures from multiple creators into the same picture to be exported. If this is the case, it is important to make sure that all of your begin comments have matching end comments before attempting to insert another picture. If this is done, you can insert the imported picture without having to worry about the comments it contains. If all of your begin/ends are matched, you can assume that the imported picture will be just as valid.
On the other hand, if you have a begin comment, and want to insert a picture before inserting the appropriate end comment, you must parse the picture to be inserted to make sure it is not using the same comment pair. If it is, and you insert it, you will have problems.
So make sure that all your begins and ends are matched, and don't try to insert other pictures between begin/end pairs of comments. If you find that you have to insert a picture between a pair of begin/end comments, you must parse the picture to be inserted to make sure that it does not use the same comments.
Penalty for Quickdraw - Clipping
Here's a subtle fact about Quickdraw pictures. If you call OpenPicture, and then record some drawing operations, and you don't explicitly specify a clipping region, Quickdraw will specify one for you. In fact, Quickdraw will use the last clip region stored in the GrafPort that you are using when you call OpenPicture. This has been a surprise to many a developer when they record a picture, and a big piece of it ends up missing when they draw it. This isn't specific to print time, it can happen on the screen too, but it happens enough that it's worth mentioning.
D' Resolution
If you've read Technical Note #275, Features of 32 Bit Quickdraw 1.2, you probably noticed the new font and resolution information. Basically, fonts are now stored in pictures by name, not by ID. This means that fonts stored in pictures will be displayed correctly on any Macintosh without fonts remapping to other faces. The other new addition to the picture format is horizontal and vertical resolution information. Applications that create pictures using the new OpenCPicture call will be able to tell Quickdraw the native resolution of the picture data. So if you're a scanner that is scanning at 200 dpi, you will be able to store your data at 200 dpi (instead of scaling it to 72 dpi first). When an application subsequently opens the picture, it can determine the picture's resolution and take the necessary steps to display it correctly (ie. scaling down for display on the 72 dpi screen, or scaling up for display on 300 dpi devices like the LaserWriter).
Conclusion
Quickdraw pictures can be used successfully at print time, if you avoid the problems described above. Although there is a little overhead required by some of the workarounds, most are very simple to implement, and will help you avoid future compatibility problems.
Further Reference:
• PostScript Language Reference Manual, Adobe Systems Inc.
• LaserWriter Reference Manual, Addison-Wesley
PostScript is a registered trademark of Adobe Systems Incorporated.
PR 22 - pIdle Proc (or how to let users know what’s going on during print time…)
Printing
Written by: Pete “Luke” Alexander April 1991
This Technical Note discusses how to defensively program a pIdle procedure to work with the majority of print drivers in existence today, and how to install it at print time.
Introduction
When using a pIdle procedure at print time, there are a few things that one should remember to be compatible with the printer drivers that are available today. This Technical Note discusses installing a pIdle procedure at the right time and the things to remember when writing one.
Installing The pIdle Proc
Let's start by installing the pIdle procedure at the right time. You must install your pIdle procedure into the print record before calling PrOpenDoc. If you do not install your pIdle procedure before your call to PrOpenDoc, the printer driver does not give the application's pIdle procedure any time. The following code fragments demonstrate installing the pIdle procedure in the right place:
MPW Pascal
<< more print loop would appear above, see Tech Note #161 for details >>
{** Install a pointer to your pIdle proc into your print record. **}
<< more print loop would follow below, see Tech Note #161 for details >>
For a complete printing loop that handles errors at print time and makes all of the right calls to the Printing Manager, refer to Technical Note #161, A Print Loop That Cares...
Things To Remember About pIdle Procedures
It is extremely important to design and code your pIdle procedure as defensively as possible, thereby making sure that it works with as many printer drivers as possible. This section details a few things to remember when creating pIdle procedures.
Saving And Restoring The Current Port
It is extremely important to save the printer driver’s GrafPort, upon entry to your pIdle procedure and restore it upon exit. Why? If you do not, the printer driver would draw into the GrafPort of your dialog box instead of it's GrafPort,which will cause some bad results. To save the printer’s GrafPort, you should call _GetPort when entering your procedure. Before you exit your procedure, you would call _SetPort to set the port from your dialog box back to the printer driver’s GrafPort (i.e., the one you saved with _GetPort).
Saving And Restoring The Printer Driver’s Resources
If the application changes the resource chain within it's pIdle procedure, you want to save and restore the printer driver’s resource chain. Why? Some printer drivers assume that their resource chain does not change, but this may not be true when the driver calls the pIdle procedure installed by the application at print time. To accomplish this task, call _CurResFile, saving the ID of the printer driver’s resource file at the beginning of your pIdle procedure. When you exit from your pIdle procedure, restore the resource chain back to the printer driver’s resource chain with a call to _UseResFile.
At this point, you might be wondering what might change the resource chain. If you called _OpenResFile or _UseResFile (anything that would change the value of the low memory global TopMapHdl) within the application's pIdle procedure, the chain would be changed. If you are not changing the resource chain, these calls would not be needed.
Handling Errors From Within A pIdle Procedure
You should avoid calling PrError within your pIdle procedure; errors that occur while it is executing are usually temporary, and serve only as internal flags for communication within the printer driver—they are not intended for the application. If you absolutely must call PrError within your idle procedure, and an error occurs, never abort printing within the idle procedure itself. Wait until the last called printing procedure returns, then check to see if the error still remains. Attempting to abort printing within an idle procedure is a guarantee of certain death.
Canceling Or Pausing The Printing Process
If you install a procedure for handling requests to cancel printing, with an option to pause the printing process, beware of timeout problems when printing to the LaserWriter. Communication between the Macintosh and the LaserWriter must be maintained to prevent a job or a wait timeout. If there is not any communication for a period of time (over two minutes), the printer times out and the print job terminates due to a wait timeout. Or, if the print job requires more than three minutes to print, the print job terminates due to a job timeout. Since, there is not a good method to determine to what type of printer an application is printing, it is probably a good idea to document the possibility of a LaserWriter timing out for a user who chooses to select “pause” for over two minutes.
Some Printer Drivers Do Not Support pIdle Procedures
Some printer drivers do not support pIdle procedures, as they prefer to handle the pIdle procedure in their own manner without giving an application’s pIdle procedure any time. This situation should not be a problem as long as you do not assume that your pIdle procedure is always called at print time. Therefore, you should only create your pIdle procedure to display the dialog box and respond to a user pausing, continuing, or canceling a print job.
Conclusion
When installing your pIdle proc, it must be installed before the application calls PrOpenDoc. You want to make sure that you save and restore the GrafPort , upon entry and exit of your pIdle procedure, to make sure that the printer driver will image into the correct port during the print job. Finally, if you are changing the resource chain by calling _OpenResFile or _UseResFIle, you want to make sure that you save and restore the resource chain.
Further Reference:
• Inside Macintosh, Volume II, The Printing Manager
• Technical Note M.IM.PrintLoop —
A Print Loop That Cares...
PR 23 - Position-Independent PostScript
Printing
Revised by: March 1988
Written by: Scott "ZZ" Zimmerman
This technical note describes a method for inserting position-independent PostScript into QuickDraw pictures.
There is a problem with pictures that contain PostScript code. Sometimes the PostScript code that is inserted into the picture is dependent on the position of the picture on the page. The problem arises when these pictures are cut or copied from their original position, and pasted into another position or even into another document. The PostScript code will not know the new location of the picture, and will not execute correctly.
The solution for this problem, is to provide some way for the PostScript code to determine the current location of the picture relative to the page that it is being printed on. This is done by inserting QuickDraw calls to position the LaserWriter’s pen before inserting the position dependent PostScript code. When the PostScript in the picture is executed, the LaserWriter’s pen location will be in a location relative to the position of the picture on the page.
The following example illustrates a method for positioning the LaserWriter’s pen before inserting any PostScript code into the picture. The method uses QuickDraw calls to position the LaserWriter’s pen, and will work with any application that supports QuickDraw pictures. Applications do not have to be changed to be able to print pictures which use this technique; normal calls to DrawPicture will work.
The following code fragment will create a picture that contains PostScript to draw a rectangle around the picture’s frame:
FUNCTION CreatePicture(pictureRect: Rect): PicHandle;
(* Kill off the Handle we created and close the picture. *)
DisposHandle(PSHandle);
ClosePicture;
END; (* CreatePicture *)
See the LaserWriter Reference Manual for more information about PicComments. See the PostScript Language Reference Manual for more information about the currentpoint operator.
There are some important guidelines to follow when sending PostScript directly to the LaserWriter. See the PostScript Commands section of Technical Note #91, for a complete description of these guidelines.
Further Reference:
• The Print Manager
• QuickDraw
• LaserWriter Reference Manual
• Technical Note M.IM.PictComments —
Optimizing for the LaserWriter—PicComments
• PostScript Language Reference Manual, Adobe Systems
• PostScript Language Cookbook and Tutorial, Adobe Systems
PR 505 - PostScript Q&As
Printing
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
PostScript documentation
Date Written: 7/13/90
Last reviewed: 8/1/92
Adobe Systems publishes three great manuals that can help you with PostScript commands: PostScript Language Reference Manual, PostScript Language Tutorial, and PostScript Language Cookbook. You can find these manuals at just about any bookstore, or you can order them through APDA by calling (800) 282-2732 or writing them at:
Apple Computer, Inc.
20525 Mariani Avenue, Mail Stop 33-G
Cupertino, CA 95014
AppleTalk packets and PostScriptHandle PicComment
Date Written: 8/7/90
Last reviewed: 8/1/92
What’s the optimal amount of PostScript to pass in PicComment 192 for AppleTalk packet throughput?
___
There is no one optimal size that your PostScriptHandle PicComment should be for AppleTalk packets. The LaserWriter driver sends different amounts of PostScript, depending on the amount of memory that is available. It could send as little as 1K or as much as 4K bytes of PostScript at one time. The driver is using four 4096-byte buffers to send the data, and the amount of data that it sends from a particular buffer is completely memory dependent. In the general case, it does not send any data until a particular buffer is full. The driver does not control the number of packets that are sent over AppleTalk. When PAP receives the PostScript, it packages up the PostScript and sends it down to the LaserWriter. In general, DTS recommends that applications send a PostScript handle that contains 4096 bytes of data.
Using PostScript to control LaserWriter serial I/O
Date Written: 12/6/90
Last reviewed: 3/28/93
Where can I find information on using PostScript to control a multi-tray sheet feeder by communicating through the LaserWriter serial port?
___
You need to talk with Adobe on how they control the serial chip with PostScript. They use some undocumented PostScript calls to control the serial chip on the controller board of the LaserWriter. They can be reached at:
Adobe Systems, Inc.
1585 Charleston Road
Mountain View, CA 94039
or
AppleLink Address: ADOBE.SUPT (Tech Support)
Details about installing feeder resources for Macintosh into the LaserWriter driver 7.0 dialogs are available in the Macintosh Technical Note “Feeder Fodder.”
How to inhibit LaserWriter test page
Date Written: 12/24/90
Last reviewed: 8/1/92
How do I inhibit the test page on a LaserWriter?
___
1. Use the LaserWriter Utility to turn the startup page off (best), or
2. Download the following PostScript commands:
serverdict begin 0 exitserver
statusdict begin
false setdostartpage
end
The above commands can be downloaded from your Macintosh to the printer several ways:
1. Use the menu command in the LaserWriter Utility that comes with System 7 (easiest).
2. You can use a PostScript utility like LaserTalk from Emerald City Software.
3. The PostScript Language facility in Microsoft Word.
4. PSDump or PSsend
5. Use the PostScript utilities in the Developer CD’s PostScript Utilities folder.
6. Use the methods described in the Macintosh Technical Note #91.
When you issue the commands, your LaserWriter will probably crank and grind for a few seconds (it has to re-program the EPROM inside) then after the lights stop flashing, your LaserWriter will no longer spit out the startup page until you enable the setdostartpage flag again (in other words, this isn’t a permanent change).
For more information on the above subject we recommend the PostScript Language Reference Manual by Adobe Systems.
Where to find Encapsulated PostScript file (EPSF) documentation
Date Written: 4/5/91
Last reviewed: 8/1/92
I’m developing an application that creates pictures containing both QuickDraw and PostScript (as picComments). How do I create an encapsulated PostScript file (EPSF)? Is the format explained anywhere, and is it possible to convert the QuickDraw into PostScript directly in the application?
___
Adobe’s EPSF format is documented in the new (2nd) edition of Adobe’s PostScript Language Reference Manual (Addison-Wesley) and in “Adobe Document Structuring Conventions,” available from Adobe Systems. Adobe’s references explain how to structure your comments in your PostScript file so that other applications know how to read it.
You probably don’t want to try to convert QuickDraw to PostScript directly in your application, although it is possible. Basically, it involves translating each QuickDraw command into PostScript, which is what the LaserWriter Driver does. It’s a lot of work and makes you dependent on the QuickDraw features being used, which can be a problem when new features are added.
PostScript setprintername doesn’t accept colons in string
Date Written: 4/16/91
Last reviewed: 8/1/92
Why can’t I use a colon with the PostScript command setprintername to change the Personal LaserWriter NT network name and type, but it works with the LaserWriter II?
___
The reason that the Personal LaserWriter NT is not accepting the PostScript you’re sending is not because of a bug, but rather a bug fix. Colons have never been acceptable in the string passed to setprintername. (See page 296 of Adobe’s PostScript Language Reference Manual.) The reason the call works in older LaserWriters but not the Personal LaserWriter NT is that the NT has a newer version of PostScript in it. Older versions of PostScript didn’t enforce Adobe’s printer naming conventions. The setprintername restrictions have been enforced in more recent versions, however. Therefore, colons are not allowed.
If you need to change the LaserWriter network type, it can be done with the AppleTalk-type parameter described on page 105 of Apple’s LaserWriter Reference manual. This method will work on the new printers as well as the old.
PostScript code for renaming LaserWriter
Date Written: 6/5/91
Last reviewed: 8/1/92
In checking a printer name to ensure an even number of characters, does the LaserWriter Namer append a space to the end if the total is odd?
___
In a nutshell, yes. The Namer does indeed append an extra space to odd-length printer names. The reasoning behind this is to be compatible with some of the old 68000 processors. The addressing scheme of these old processors requires that memory be accessed by even-numbered addresses. To get at a piece of memory located after an odd-length LaserWriter name, you would need to get an odd-numbered address (which is not directly possible). This obviously can be worked around, but the authors of the Namer felt it best to avoid this altogether by padding the name when necessary. It’s worth noting that the version 7.0 Namer does this too.
If you’d like to work around this, here’s an alternate method for renaming the printer:
The printer name is stored as a string in the persistent parameters of all LaserWriters. This string can be changed by the following PostScript program:
serverdict begin 0 exitserver
statusdict begin (NEWNAME) setprintername
Replace NEWNAME with the name you want for the printer. The name can be up to 31 characters and should consist entirely of printing characters--the “@” and “:” cannot be used. Note that parentheses are required.
This program can be downloaded to the LaserWriter in batch or interactive mode. In interactive mode you should type both command strings on the same line before pressing Enter, because exitserver disconnects you from the printer. When you reestablish your connection to the printer, the name will be changed.
Make sure you have selected the correct LaserWriter using the Chooser before downloading this program, so you don’t rename the wrong LaserWriter.
Printing 2-byte bitmap fonts on PostScript LaserWriters
Date Written: 9/29/91
Last reviewed: 8/1/92
The Macintosh Technical Note #91 refers to using QuickDraw picComments to print rotated text, but this doesn’t work for 2-byte Japanese text. Is there a recommended workaround for this problem?
___
Two-byte characters don’t print when using the TextBegin, TextCenter, and TextEnd PicComments because of the structure of the data that is being sent in the comments. The LaserWriter driver uses these PicComments to rotate text, and it is not expecting the data to be in the 2-byte format. The 2-byte format is converted to bitmap data by the driver, so the data is ignored and nothing is printed. The driver is expecting the comments to contain “normal” text.
Since the data is in bitmap format, you can use the RotateBegin and RotateEnd PicComments to perform the rotation, as follows:
RotateBegin
DrawString (...); << To rotate your 2 byte Japanese characters
RotateEnd
The tricky part of the solution is that you only need to use this approach when you’re printing 2-byte Japanese bitmap fonts. Therefore, you’ll need to check the type of data that is to be printed, and send the appropriate PicComments.
You can also use the following PicComments to hide the QuickDraw representation of the rotated text, as shown below:
PostScriptBegin
CopyBits (...); << For the QuickDraw printers
PostScriptEnd
The PostScriptBegin and PostScriptEnd PicComments are used to hide the CopyBits calls from the LaserWriter driver in a device-independent manner. When you use PostScriptBegin, the LaserWriter driver doesn’t interpret any QuickDraw calls until it finds the PostScriptEnd PicComment. Since QuickDraw printer drivers do not understand the PostScriptBegin and PostScriptEnd PicComments, they ignore the PicComments, and the CopyBits call is used to perform the rotation of your text.
There is one problem with the previously mentioned solution: If you record the previous PicComments into a picture and print it, everything will print exactly as expected, but if you display the picture into a window, you’ll see the unrotated version of the text. This is definitely a bug, which has been reported, but it won’t be fixed in the very near future. So, what do you do in the meantime? You have the following two options:
1) Do not export pictures that contain this type of rotated data.
2) Export the picture with this problem, and explain the problem to your users.
Neither solution is ideal. Quite a few applications out in the market today have decided to use idea #2.
Macintosh spooling should be device independent
Date Written: 1/1/90
Last reviewed: 8/1/92
How can I tell whether the chosen printer supports PostScript or not?
___
There is no supported method for determining whether or not a device speaks PostScript. Apple engineering would like all Macintosh applications to spool both PostScript and QuickDraw regardless of the chosen printer, so that the spool file is device-independent and can be redirected after spooling. Since engineering is strongly encouraging developers to spool both models, it is doubtful that Apple will be providing a query mechanism in the future. The best method is to assume PostScript is available, and format your document based on information returned by PrGeneral and the print record.
LaserWriter driver and PostScript showpage
Date Written: 1/1/90
Last reviewed: 6/14/93
When I print a PostScript file, why do I get one blank page for every page printed?
___
Your PostScript file probably contains calls to the PostScript showpage operator. This operator is also called by the Macintosh LaserWriter driver when it receives the PrClosePage call. The solution to this problem is to strip showpage calls from the file before sending the file to the LaserWriter. If you send one QuickDraw graphic at the beginning of each page,
MoveTo(-32000, -32000);
Line(0, 0);
the LaserWriter driver will handle calling showpage for you. Versions of the LaserWriter driver before 5.2 do not send the showpage operator unless a QuickDraw drawing routine (that is, something other than PicComment) is sent. This is why you need to do the MoveTo/LineTo. Versions 5.2 and later will send the showpage operator whether any QuickDraw has been sent or not. Since you can’t be sure whether showpage will be sent for you or not, the best solution is to let the LaserWriter driver send it.
PicComment TextIsPostScript versus PostScriptHandle
Date Written: 1/1/90
Last reviewed: 6/14/93
When using the Macintosh TextIsPostScript picture comment, my PostScript code is also “printed” on the ImageWriter. Why isn’t it ignored?
___
One of the comments, TextIsPostScript, allows the application to use the QuickDraw DrawString procedure to send the PostScript code. This method is no longer recommended, as it is not device independent. If you use the TextIsPostScript comment when printing to a non-PostScript device, the PostScript code will be printed as text. On an ImageWriter, the entire PostScript program will be printed as one line on the page, because the TextIsPostScript PicComment is ignored by the ImageWriter drivers, and the DrawString calls are executed normally.
To avoid this problem, developers should use the PostScriptHandle comment. This comment accepts a handle as a parameter. The handle points to the PostScript code that you want to download. This comment is better for two reasons:
First, there is no QuickDraw/PostScript interaction. For example, early versions of the LaserWriter driver sent the strings passed to DrawString as-is. The latest version of the driver appends carriage returns onto those strings. If your application is expecting the DrawString calls to work one way or another, you might get a surprise.
The other reason for using the PostScriptHandle comment involves device independence. When a non-PostScript driver sees the PostScriptHandle PicComment, it not only ignores the comment, but also ignores the PostScript data that was passed to the comment, so nothing prints if you use the PostScriptHandle comment on a non-PostScript driver.
X-Ref:
Macintosh Technical Note “How To Produce Continuous Sound Without Clicking”
How to send PostScript from the Macintosh to the LaserWriter
Date Written: 5/3/89
Last reviewed: 6/14/93
How do I send PostScript from the Macintosh to the LaserWriter?
___
The best method for sending PostScript to the LaserWriter is to use the PostScriptHandle (kind = 192) PicComment documented in the Macintosh Technical Notes “Optimizing for the LaserWriter” and “Picture Comments—the Real Deal.” There are a few PicComments for sending PostScript to the LaserWriter, but the easiest to use and most problem free is the PostScriptHandle comment. One model is:
PrOpenPage(...)
{ Send some QuickDraw so that the Printing Manager gets a }
The described fragment prints a line on any type of printer, PostScript or not. The first MoveTo/Line combination is required to give the LaserWriter driver a chance to define a clipping region. The LaserWriter driver replaces the grafProcs record in the GrafPort returned from PrOpenDoc. In order for the LaserWriter driver to get execution time, you must execute a QuickDraw drawing routine that calls one of the grafProcs. In this case, the MoveTo/Line combination is sent firing the StdLine grafProc. When StdLine executes, it notices that the GrafPort has been reinitialized, and therefore initializes the clipping region for the port. Until the MoveTo/Line combination is executed, the clipping region for the port is set to (0,0,0,0). If PostScript code is sent via the PostScriptHandle PicComment before executing any QuickDraw routines, all PostScript operations will be clipped to (0,0,0,0). The next thing to do is send the PostScriptBegin PicComment. This comment is recognized only by PostScript printer drivers. When the driver receives this comment, it saves the current state of the PostScript device (by executing the PostScript gsave operator), then disables all QuickDraw drawing operations. This way, the QuickDraw representation of the Graphic will be ignored by PostScript devices. In the previous example, the MoveTo/LineTo combination is only executed on non-PostScript devices. The next comment is the PostScriptHandle PicComment. This tells the driver that the data in thePSHandle is to be sent to the device as PostScript code. The driver then passes this code unchanged to the PostScript device for execution. The PostScriptHandle PicComment is only recognized by PostScript printer drivers. The last PicComment, PostScriptEnd, tells the driver to restore the previous state of the device (via a PostScript grestore call), and to enable QuickDraw drawing operations. Since all PicComments are ignored by QuickDraw devices, only the QuickDraw representation is printed. Since PostScriptBegin tells PostScript drivers to ignore QuickDraw operations, only the PostScript representation is printed on PostScript devices. This is a truly device-independent method for providing both PostScript and QuickDraw representations of a document.
Converting PostScript file to Encapsulated PostScript
Date Written: 5/19/92
Last reviewed: 6/14/93
What needs to be added to an ordinary PostScript file to convert it to an Encapsulated PostScript (EPS) file?
___
The latest Adobe “red book,” PostScript Language Reference Manual (2nd Edition), has the information you need. Appendix H: Encapsulated PostScript File Format Version 3.0 describes the contents of an EPS file. Appendix G: Document Structuring Conventions specifies the single-page document PS file, which must include at least these two comments:
%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: llx lly urx ury
You might find the following references from Adobe useful as well:
Adobe Illustrator Document Format Specification v2.0 (Adobe Technical Note #LPS5007)
Macintosh Technical Note “Picture Comments—The Real Deal” (formerly #91) contains sample code for using all of the supported Picture Comments.
Printing halftone images on the LaserWriter
Date Written: 5/3/89
Last reviewed: 8/1/92
How can I print halftone images on the LaserWriter? How do I use the PostScript “image” operator for printing halftones?
___
The PostScript “image” operator is similar to the Macintosh QuickDraw CopyBits routine. It can be used by converting a QuickDraw bitmap/pixmap into an image call. This is only necessary if you are printing color information with a PostScript printer driver that doesn’t support Color QuickDraw. LaserWriter drivers up to 5.2 do NOT support Color QuickDraw. If you want to send color information to a LaserWriter with version 5.2 of the driver, you will have to convert your color images into image commands. Before doing this it is important to check the GrafPort returned by PrOpenDoc. If this port is a Color QuickDraw port (for example, portBits.rowBytes < 0) then you should use Color QuickDraw commands and let the driver handle the conversion to PostScript.
About image: The PostScript image operator is used to send bitmaps or pixmaps to the LaserWriter. The image operator can handle depths from one to eight bits per pixel. Our current LaserWriters can image only about 16 shades of gray, but the printed page will look like there’s more. Because the image operator is still a PostScript operator, it expects its data in the form of hexadecimal bytes. The bytes are represented by two ASCII characters(“0”-”9,” “A”-”F”) per byte. The image operator takes these parameters: width height depth matrix image-data
The first three are the width, height, and depth of the image, and the matrix is the transformation matrix to be applied to the current matrix. See the PostScript Language Reference Manual for more information. The image data is where the actual hex data should go. Instead of inserting the data between the first parameters and the image operator itself, it is better to use a small PostScript procedure to read the data starting right after the image operator. For example:
In the preceding example, the width of the image is 640, the height is 480, and the depth is 8. The matrix (enclosed in brackets) is set up to draw the image starting at QuickDraw’s 0,0(top left of page), and with no scaling. The PostScript code (enclosed in braces) is not executed. Instead, it is passed to the image operator, and the image operator calls it repeatedly until it has enough data to draw the image. In this case, it is expecting 640*480 bytes. When the image operator calls the procedure, it does the following:
1. Pushes the current file which in this case is the stream of data coming to the LaserWriter over AppleTalk. This is the first parameter to the readhexstring operator.
2. Pushes picstr. picstr is a string variable defined to hold one row of hex data. The PostScript to create the picstr is:
/picstr 640 def
3. readhexstring is called to fill picstr with data from the current file. It begins reading bytes which are the characters following the image operator.
4. readhexstring leaves the string we want, and a boolean that we don’t want, on the stack, so we do one pop to kill off the boolean. Now the string is left behind for the image operator to use.
Using the above PostScript code you can easily print an image. Just fill in the width height and depth, and send the hex data immediately following the PostScript code.
Setting up for image: Most of the users of this technique are going to want to print a Color QuickDraw pixmap. Although the image command does a lot of the work for you, there are still a couple of tricks for performance:
Assume the maximum depth: Since the current version of the image operator has a maximum depth of eight bits/pixel, it is wise to convert the source image to the same depth before imaging. You can do this very simply by using an offscreen GrafPort that is set to eight bits/pixel, and then using CopyBits to do the depth conversion for you. This does a nice job of converting lower resolution images to 8 bits/pixel.
Build a color table: An eight bit deep image can use only 256 colors. Since the image that you are starting with is probably color, and the image you get will be grayscale, you need to convert the colors in the source color table into PostScript grayscale values. This is actually easy to do using the Color Manager. First create a table that can hold 512 bytes. This is two bytes for each color value from 0 to 255. Since PostScript wants the values in ASCII, you need two characters for each pixel. Now loop through the colors in the color table. Call Index2Color to get the real RGB color for that index, and then call RGB2HSL to convert the RGB color into a luminance value. This value is expressed as a SmallFract which can then be scaled into a value from 0 to 255. This value should then be converted to ASCII, and stored at the appropriate location in the table. When you are done, you should be able to use a pixel value as an index into your table of PostScript color values. For each pixel in the image, send two characters to the LaserWriter.
Sending the data: Once you have set up the color table, all that’s left to do is loop through all of the pixels, and send their PostScript representation to the LaserWriter. There are a couple of ways to do this. One is to use the low-level Print Manager interface and stream the PostScript using the stdBuf PrCtlCall. Although this seems like it would be the fastest way, the latest version of the LaserWriter driver (5.0) converts all low-level calls to their high level equivalent before executing them, so the low-level interface is no longer faster than the high level. Another way is to use the high-level Print Manager interface, and send the data via the PostScriptHandle PicComment. This enables you to buffer a large amount of data before actually sending it. Using this second technique, you should be able to image a Macintosh II screen in about 5 minutes on a LaserWriter Plus, and about 1.5 minutes on a LaserWriter II NTX.
X-Ref:
PostScript Language Reference Manual, Adobe Systems.
PR 510 - Printer Driver Q&As
Printing
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As in this Technical Note:
Defining default LaserWriter Pro paper tray
Code for getting selected printer name
StyleWriter II driver differences
Determining printer font
Defining default LaserWriter Pro paper tray
Date Written: 5/17/93
Last reviewed: 5/19/93
How can I set the default paper tray on my LaserWriter Pro to something other than the 250-sheet cassette?
___
The paper tray selection in the printing job dialog is not “sticky”; it’s not preserved from job to job. In LaserWriter 7.2, the choice of one of the two paper trays is stored in the print record. You may be able to hack out where this is stored and define a new “default” print record by changing the PREC(0) resource, but this is completely unsupported and you do so at your own risk.
In LaserWriter driver 8.0, the default paper trays are defined in the PostScript Printer Definition (PPD) file. If you’re sufficiently adventurous, you can edit the PPD file for your printer to change these relationships. LaserWriter 8.0 requires files that meet the PPD 4.0 specification, available from Adobe’s Developer Support program.
Code for getting selected printer name
Date Written: 3/10/93
Last reviewed: 6/24/93
How do I get the name of the currently selected printer, for Systems 6 and 7?
___
The printer driver has a resource of type PAPA, ID -8192, which contains the name of the selected printer and the network object type. This resource is amazingly easy to get; you don’t even need to know where the printer driver resource file is located. Just call PrOpen. The Printing Manager will open the currently selected printer driver for you, and place it at the top of the resource chain. Then see whether you can GetResource for 'PAPA' ID -8192, and viola—you have a handle to the printer name.
This will work under both System 6 and System 7. However, under QuickDraw GX, this method is subject to change. Also note that this is a read-only field; changing it doesn’t necessarily change the selected printer.
The printer name is the first string, and the object type is packed right up against it. If all you want is the name, then here’s a C routine that will provide it:
My application won’t print to a StyleWriter II although it works fine on all other printers including the original StyleWriter. Can you give me any suggestions? What’s different about the StyleWriter II?
___
The StyleWriter II driver is a member of the “GrayShare” driver family, with new features such as support for grayscale printing (if Color QuickDraw is available) and printer sharing over the network. Its internal architecture is very different from previous printer drivers. In spite of thorough compatibility testing, some problems have shown up since the first release. In many cases, the driver revealed weaknesses in the applications themselves; for some other problems, a solution will be incorporated in the next release of the driver. Here are currently identified problem areas with the StyleWriter II driver:
• The StyleWriter II driver handles memory differently. For example, if an application has a dereferenced handle to an unlocked block while CopyBits’ing into the printing port, this may work fine on all other printer drivers, but the block is likely to move with the StyleWriter II driver, and the results are unpredictable.
• The StyleWriter II driver maintains its own A5 world internally. If an application installs a growZone proc and forgets to set up its own A5 in the growZone proc (ignoring the Technical Note “Register A5 Within GrowZone Functions”), the growZone proc may get called with the StyleWriter II driver’s A5, which obviously is bad for the survival of both the application and the printer driver.
• The StyleWriter II driver calls the pIdleProc (in the job subrecord of the print record) more often than other printer drivers; in particular, it may be called during execution of PrOpenDoc. If an application reloads a print record previously used, containing an old (now invalid) pointer to an idleProc, and doesn’t update the pIdleProc field before calling PrOpenDoc, disaster is very likely. Note that the TechNote “pIdle Proc (or how to let users know what’s going on during print time)” recommends installing the idleProc before PrOpenDoc.
• Like most other QuickDraw printer drivers, the StyleWriter II driver uses algorithms built into QuickDraw for rasterizing picture elements like ovals and arcs, but not necessarily with the same sequence of coordinate transformations. Because of the 360 dpi resolution, internal computations with the transformed coordinates may hit the 16-bit integer limitation of QuickDraw, and reveal bugs in the old QuickDraw routines that have never been discovered so far.
• The StyleWriter II driver (version 1.0) contains STR# resources with positive ID numbers that may conflict with STR# resources in an application. This will be fixed in the next release of the driver.
• Under certain circumstances, the StyleWriter II driver seems to have trouble with a PmForeColor call. This is under investigation and will be fixed.
Determining printer font
Date written: 1/26/93
Last reviewed: 4/26/93
Our application must print with Helvetica if available, and Geneva if not. How can I determine which fonts are available in a printer? I’ve been using the wDev field in the print record and assuming that a value of 3 means Helvetica will print correctly, but according to “Print Hints: Top 10 Printing Misdemeanors” in develop issue 12, this isn’t dependable.
___
There’s no procedural way to ask a printer if it even has built-in fonts, let alone what they are. By using wDev == 3, you’ve been assuming that all PostScript printers have built-in Helvetica fonts, and while most do, some do not. There are several things you can do, though.
First of all, you should know about font substitution. If this option is checked in the LaserWriter driver (it’s checked by default), you’ll get Helvetica instead of Geneva when drawing in Geneva on PostScript printers that have Helvetica. The driver adjusts all the font metrics so the widths match what you see on the screen. In some versions of the LaserWriter driver, you’ll get Geneva instead—but only if the Geneva TrueType font is available, so you get excellent quality either way. This might be enough to solve your entire problem.
If not, there’s the new PSWriter driver on AppleLink and on the Developer CD Series disc. PSWriter uses PostScript Printer Definition (PPD) files and supports a new PrGeneral call that lets you obtain the PPD file so that you can know things about your target printer, including what fonts are installed. While this might give misleading results if the user has never set up the printer (and is using the Generic PPD), it should be just fine in all other cases.
There’s no way to tell whether the user has downloaded a font to the PostScript printer, making it available, because it won’t be in the PPD file and there’s no way to ask the printer.
Where to get PostScript Printer Definition (PPD) files
Date written: 2/9/93
Last reviewed: 4/1/93
Where can I get PostScript Printer Definition (PPD) files for Apple’s PostScript printers?
___
Apple recently made PPD files for its printers—past and present—available. You can find them on AppleLink in the Software Sampler: Apple SW Updates: Macintosh: Printing Software: PPDs folder.
PPD files for non-Apple PostScript printers with ROMs by Adobe Systems are available from a clearinghouse maintained by Adobe. To request a PPD file from Adobe, send an internet message to “ps-file-server@adobe.com” with the subject “help”. Adobe’s list server will respond with mail about items available on the list server and how you can get them.
LaserWriter Driver 7.2 isn’t ColorSync aware
Date written: 1/11/93
Last reviewed: 4/1/93
Is LaserWriter Driver 7.2 ColorSync aware?
___
No, LaserWriter Driver 7.2 isn’t ColorSync aware. Apple plans to add ColorSync capability to future versions of the LaserWriter driver, but we don’t have any more information or details than that.
Printing nonstandard page dimensions
Date written: 1/14/93
Last reviewed: 6/14/93
How do we override the standard page dimensions for the StyleWriter or, for that matter, any other printer? We want to print 5.5" x 8.5" (half letter-sized) pages.
___
The current printing architecture isn’t designed for elegant (or sometimes any) communication from your program to the printer driver. All the calls you normally make (such as PrOpenDoc, PrOpenPage, and PrPicFile) are handled by the printer driver and not by the Printing Manager. The drivers manage the print records, put up the dialog boxes, decide how to render graphics and everything else in between. That’s why writing a printer driver is so hard.
If you want to tell a printer driver “I want to use this size paper,” that driver has to implement a driver-specific way to do it or it can’t be done. There’s no PrSetPageSize call in the current printing architecture. In fact, trying to print outside the rPage rectangle can cause physical damage on some printers, so customizing page sizes is not something you want to enter into lightly.
You can’t change the paper size in the print record, because most drivers don’t look there to see what size you want; instead they tell you what size you get in that field. The same is true for most of the print record: Unless Inside Macintosh tells you that you can write to that field, you can’t (or doing so won’t do any good).
The StyleWriter in particular has very limited paper options; it handles letter, legal, A4, and B5 paper sizes—and that’s it. If you try to print other sizes, the hardware reports an error. This is pretty common in these days of very low-cost high-quality printers; features that you as a programmer would want are jettisoned to keep the cost down. Most low-cost laser printers have limited paper-handling capabilities as well. If you try to print continuous feed paper on a StyleWriter, you’ll get a paper jam error after about 15 inches of paper pass through (long enough for the printer to realize it’s not legal-sized paper). Your half-letter size pages are too short for the printer to use.
While QuickDraw GX has a much more robust printing architecture, that doesn’t help right now for the current world. Something that you may wish to consider is making forms available on regular letter-sized paper, with two per sheet. That size paper works in every printer sold in the United States and could be perforated and detached by the customers.
All of Apple’s LaserWriter printers have excellent paper handling but cost relatively more. The best way to handle custom page printing across all printers is to place your custom-sized image on an 8.5" x 11" page and provide special paper.
Many, but not all, of Apple’s printers support a PREC 3 resource that contains extra page sizes in a custom format. The format of this resource is as follows:
PageSizeRecord = RECORD
numItems: INTEGER;
botRights: ARRAY [0..NumPageOptionsZB] OF Point;
titles: PACKED ARRAY [0..0] OF CHAR;
END;
numItems is the number of page sizes displayed in the dialog. The maximum number is six (since there is only room for six radio buttons in the Page Setup dialog). botRights is an array of points specifying the bottom right corner for each page. This corner is specified in 120ths of an inch, and it should define the physical size of the paper, not the imageable area. For example, U.S. Letter is specified as:
bottom = 1320(11"), right = 1020(8.5").
titles is a packed array of page size names. Each name begins with a length byte, followed by the number of characters specified by the length byte. The strings are packed, so string1[LENGTH(string1)+1] would reference the length byte of string2.
To use this mechanism, your application should create a PREC resource with an ID = 4. You can do this very easily by copying PREC(3). Once you have a copy, you can modify the page sizes as you wish. When the printer driver is called to display the Page Setup dialog, it will first look for PREC(4). If found, it will use it to define the page sizes. If not found, the driver will use the standard sizes in PREC(3).
This method only works with some of Apple’s drivers; the LaserWriter driver ignores these resources. If you decide to implement this feature in your application, be sure to warn your users that custom page sizes may not be available on all devices.
Use this feature at your own risk; it’s very likely not to work under QuickDraw GX and later system software.
The userdict definitions in my LaserWriter driver 'feed' resource (written per the Macintosh Technical Note “Feeder Fodder”) get wiped out after each page. At least, every time my redefined showpage is executed the variables that should have been defined the first time in userdict are unbound. What could be happening here? How should I define something in 'feed' resource code that acts like a global variable for a redefined showpage?
___
Your operators shouldn’t be wiped out with each page. You’re being loaded before the PREC 103 is loaded and before the series of save and restore operators are called, which could otherwise wipe you out. Please read the Macintosh Technical Note “The Lo Down on Dictionary Downloading” for a robust description of this process. Thus, we must look to other reasons why your operators are being wiped out.
The natural culprit is your PostScript. Are you wiping yourself out? Second, you should heed the warning on page 4 of the “Feeder Fodder” Technical Note: “As always, calling or redefining operators defined by the LaserPrep (md) dictionary isn’t supported.” You could see problems as you describe if you’re redefining LaserPrep dictionary commands. Other variables include the system software and LaserWriter driver versions that you’re using. It could be an issue with the type of LaserWriter itself.
LaserWriter driver doesn’t SwapMMUMode for 32-bit addresses
Date written: 11/17/92
Last reviewed: 6/14/93
When I use CopyBits to move a cGrafPort’s portPixMap to another cGrafPort (my printing port), it works like a charm when background printing is turned on, but when CopyBits gets called with background printing turned off, the image that prints isn’t the image at all. Why is this happening?
___
You should be aware that since you’re copying the pixels directly from the screen, the baseAddr pointer for the screen’s pixMap may be 32-bit addressed. In fact, with 32-Bit QuickDraw, this is the case. This in itself isn’t a problem, since CopyBits knows enough to access the port’s pixMap’s baseAddr in 32-bit mode, as follows:
mode = true32b;
SwapMMUMode(&mode); // Make sure we're in 32-bit addressing mode.
// Access pixels directly; make no other system calls.
SwapMMUMode(&mode); // Restore the current mode.
That’s how you’d normally handle things if you were accessing the pixels directly yourself. Unfortunately, the LaserWriter driver doesn’t know enough to do the SwapMMUMode and instead ends up copying garbage (from a 32-bit pointer stripped to a 24-bit pointer).
So why does background printing work? Because when you print in the background, everything is rolled into a PICT, which the driver saves off for PrintMonitor. Since the driver is using the standard QuickDraw picture bottlenecks to do this, and CopyBits knows to swap the MMU mode before copying the data into the picture, everything works great. Later, at PrintMonitor time, the picture is played back. Since the data is no longer 32-bit addressed, the LaserWriter driver doesn’t have to call SwapMMUMode to do the right thing; it can just play the picture back.
The solution we propose for you is something similar. At print time (before your PrOpenPage call), call OpenPicture, copy the data from the screen with CopyBits, call ClosePicture, and then call DrawPicture within your PrOpenPage/PrClosePage loop. That should do the trick.
Note that copying bits directly from the screen is not something we recommend. Unless you have no alternative, you should always copy from the original source of the data instead.
System 7.1 and printer driver compatibility
Date written: 11/18/92
Last reviewed: 6/14/93
If a user upgrades from System 7.0.x to 7.1, a new version of the PrintMonitor is installed. Will existing printer drivers behave correctly with the new PrintMonitor?
___
When a user installs System 7.1, Easy Install will update any existing printer software. If the user picks Customize, he or she must choose the printers to be updated. Only the versions of the printer drivers that come with System 7.1 should be used with 7.1. Mixing and matching is bad.
Since only Apple’s printer drivers use PrintMonitor, as long as you upgrade to System 7.1 by using the Installer there’s no issue at all.
System 6 and System 7 PrintMonitor
Date written: 11/18/92
Last reviewed: 3/1/93
Is the version of PrintMonitor that ships with System 7.1 compatible with System 6? If so, which version of the Backgrounder file would be compatible for installation with the System 7.1 PrintMonitor on System 6.x systems?
___
Only Apple’s printer drivers should be using PrintMonitor—the interface is undocumented and unsupported for other printer drivers. PrintMonitor-like things are a different story, but the specific Apple product PrintMonitor works only with Apple’s printer drivers.
While the drivers may be used under System 6, users should use the versions of Backgrounder and PrintMonitor present on the System 6.0.8 disks. Since Backgrounder and PrintMonitor are tied together, we have to recommend against using the 7.1 PrintMonitor under System 6.
Note that in all these cases, nothing was specifically added to make things not work under System 6—they simply weren’t tested and therefore can’t be supported.
Summary:
System 6: Backgrounder and PrintMonitor from 6.0.8, current driver
System 7: Use whatever the Installer installs.
Which printers to test printing
Date written: 10/1/92
Last reviewed: 11/2/92
We need to know what printers on which to test our application. It prints only pixmaps. It’s been tested on some LaserWriters. The question is, how many brands and models of printers do we need to test it on? Are there any definitive printing tests (for example, does success on a LaserWriter indicate correctness of the application)?
___
Unfortunately, there is no such thing as a universal printing test suite. Moreover, successful printing not only depends on the specific printer driver (and its version), but also on the available memory at print time, and on things like the font configuration in the system. (Given that you print pixmaps only, the last comment is not applicable. Most certainly, all potential problems will rather boil down to out of memory conditions). Also, we have seen applications with severe defects in their printing code print quite correctly to the LaserWriter, and fail only on a few other specific printers. Most other printing problems have to do with assumptions about the inner working of a specific printer driver, such as hacking the print record behind the back of the Printing Manager. In this case, trouble with at least one of the 150 plus different printer drivers out there is guaranteed!
Pragmatically speaking, you may want to test printing in priority on the printers most likely being used by your customers. Currently (as of November 1992), this is the PostScript LaserWriter (driver version 7.1.2 or later), the StyleWriter (driver version 7.2.2), the Personal LaserWriter LS (driver version 7.2), and one or another of the most popular third-party printers. In addition, we recommend that you test both on an older LaserWriter (up to the LaserWriter IInt), and the newer ones (LaserWriter IIf, IIg, NTR), because of the differences in the PostScript interpreters and gray-level support.
Where to get documentation on writing a Macintosh printer driver
Date Written: 4/9/91
Last reviewed: 8/1/92
Where can I find documentation on how to write a Macintosh printer driver equivalent to the ImageWriter or LaserWriter driver? In particular, how are Printing Manager and QuickDraw commands translated into calls to the printer driver?
___
DTS’s “Learning to Drive” document and “StdFileSaver” source code, available in AppleLink’s Developer Support folder and on the latest Developer CD Series disc, are helpful references.
Writing a Macintosh printer driver
Date Written: 5/3/89
Last reviewed: 8/1/92
I have a printer I would like to connect to the Macintosh. Where can I find information on writing a printer driver?
___
A Macintosh printer driver is more than a standard device driver. A printer driver contains code to implement all of the standard Macintosh Printing Manager routines. The code includes routines like PrOpen/PrClose, and PrOpenDoc/PrCloseDoc. A Printing Resource File contains all of the resources (including code) to implement the Macintosh Printing Manager for a particular device. A driver works best with all Macintosh applications if it supports both the high- and low-level Printing Manager interfaces, as well as the PrGeneral (IM V:410) routine and its associated opcodes.
Apple is not currently supporting the development of Chooser-selectable device drivers, at least not those that support printers. There are many reasons for this, and here are at least a few of them:
First, there is no documentation or examples available. Each of the Apple printer drivers is unique. They are all written almost entirely in 68000 assembler, and consequently, are not very easy to read. Since each driver is different, the only real documentation for how the drivers work is the source code to the driver. There is some general interface information available, but important information, like how the driver manages its print record, is described only in the source code. This information could be extracted and distilled into some kind of document, but this brings up the next problem:
The Macintosh Printing Manager is currently being revised and enhanced. These revisions will require major changes to the architecture of the Printing Manager. It’s not clear what effect these changes will have on existing printer drivers, but it is very possible that the drivers that exist today will have to be revised significantly to run under the new Printing Manager.
Apple is designing and implementing the next generation Macintosh Printing Manager. Developers’ suggestions for features are being taken into account. One of the goals of the new architecture is to make writing drivers much simpler, and to allow the sharing of code between drivers. When this architecture becomes available, Apple will reconsider its position of driver development, and will probably end up with some kind of licensing agreement for developers that want to write drivers. Until then, the preceding reasons should be adequate justification for NOT attempting a Macintosh printer driver at this time.
If you still aren’t convinced, and want to write a driver despite the challenge, see the article in the December ’88 issue of MacTutor magazine. There is an example printer driver written in C. It is just a skeleton driver, and does not handle any of the more difficult problems such as line layout, font substitution, graphics, or banding. But it is a start, and gives you a general idea of the structure of a driver. Since some developers began writing their drivers before Apple discontinued support, DTS is still answering questions concerning driver development. However, these questions are limited to those that can be answered by the DTS engineers. You should also be aware that some of the techniques used by Apple printer drivers are considered proprietary. Information on methods used for line layout as well as certain aspects of font handling will not be disclosed.
X-Ref:
Printing Manager
Changing printer driver settings without using the dialog
Date Written: 11/6/91
Last reviewed: 6/14/93
To change default printer driver settings without using the dialog, keep a copy of the print record with the various settings you want, and when you want to choose your settings, just use your special print record. The current Macintosh print architecture doesn’t allow procedural access to the print record, so you must call the dialogs once for yourself, choose your settings, and then save a copy of the print record that is produced. Some settings are job-dependent, but values that are retrieved from the print record can be changed using this technique. You can only save the settings in the Style dialog. Call PrValidate when retrieving a print record from the resource fork to make sure it is “good.”
See develop, issue #1, pages 58–65, for a complete discussion of changing default printer driver settings and sample code.
Macintosh print record wDev field
Date Written: 5/3/89
Last reviewed: 8/1/92
What is the Macintosh print record wDev value for the ImageWriter LQ, LaserWriter IISC, and AppleFax Modem?
___
Apple strongly discourages the use of the wDev field in the print record for several reasons. First, this field contains a unique ID for each printer driver on the Macintosh. Coding your application to use this ID makes it device dependent, and device dependence will cause many problems in the future as the Macintosh Printing Manager continues to evolve. Many applications currently check for wDev = 3 to determine whether or not to send PostScript. Although the Apple LaserWriter driver has a wDev of 3, third-party printer drivers for PostScript devices do not, so if your application determines whether or not to send PostScript based on wDev alone, your application may incorrectly print with QuickDraw on third-party PostScript devices. A second problem concerns spoolers and spool files. If a spooler is installed between an application and the printer driver that is going to do the printing, the application receives the wDev ID of the spooler instead of the target driver. If the application makes assumptions based on this ID, it probably gets unexpected results. In the future, it may also be possible to redirect spool files. This means that a file that was originally spooled for a PostScript printer may be redirected to a QuickDraw device. If the spool file contains only the PostScript representation of the document, it will be useless to the QuickDraw device. Despite these strong warnings, some developers are convinced they need the wDev values, and there may be some vertical applications where they’re needed. For those cases, here is the current list of wDev IDs for Apple printer drivers:
Device wDev (Hi Byte)
ImageWriter I/II: 1
LaserWriter,
LaserWriter Plus,
LaserWriter IINT,
LaserWriter IINTX: 3
LaserWriter IISC: 4
ImageWriter LQ: 5
AppleFax Modem: 10
DTS does not support the use of these constants. They are definitely subject to change.
Determining if a printer driver accepts Color QuickDraw calls
Date Written: 11/21/90
Last reviewed: 8/1/92
How do you find out if a printer driver accepts Macintosh Color QuickDraw calls?
___
Check to see if the printer driver has returned a color grafPort to your application after the call to PrOpenDoc (that is, the port that PrOpenDoc returns).
To determine if the grafPort is color, you need to check to see if rowBytes from the grafPort are less than 0. The following code fragment demonstrates this idea:
(* This function determines if the port passed to it is a color port. If *)
(* so, it returns TRUE. *)
FUNCTION ColorPort(portInQuestion: GrafPtr): BOOLEAN;
BEGIN
IF portInQuestion^.portBits.rowBytes < 0 THEN
ColorPort := TRUE
ELSE
ColorPort := FALSE;
END;
Printing in mixed Macintosh System 6/7 network
Date Written: 12/11/90
Last reviewed: 8/1/92
What is the recommended printer driver for a network environment with mixed operating systems (such as System 6.0.5 on a Macintosh Plus and 7.0 on two Macintosh IIfx systems)?
___
In a mixed 6.0.x/7.0 network we recommend upgrading all systems to the 7.0 print drivers—even systems running 6.0.x. The 7.0 LaserWriter drivers work fine under system 6.0.x and will avoid any printer reinitialization problems. Use the 7.0 printing disk (or the printing install folder), and launch the Installer from that disk. It has scripts needed to install 7.0 print drivers on a 6.0.x system. Do not select the Installer options for printers from the main 7.0 Installer, because these scripts currently are only for systems running 7.0.
If a Personal LaserWriter NTR is used on the network, you must upgrade all workstations to the 7.1.1 LaserWriter driver or later. (The NTR requires at least LaserWriter 7.1.1.)
System 7.0 LaserWriter driver & choosing nonstandard page sizes
Date Written: 4/8/91
Last reviewed: 6/14/93
With the System 7.0 version of the LaserWriter driver, when the user selects Envelope from the Page Setup dialog, the page size returned by the driver is still a standard page: 8.5 x 11. How do you recommend that applications display the page size when the user has chosen a nonstandard page size?
___
We recommend that you have the page preview show a full page instead of an envelope-sized page. The LaserWriter driver supports a large number of PostScript devices, and it can’t be sure whether the envelope will be fed on the right, left, or center of the paper tray. If you show the full page, a user can print on any device by putting the text in the correct location for that device.
Manufacturers of PostScript printers can add custom page sizes to the LaserWriter driver. If they do, the representation on the screen will be whatever they decide to define. Applications should not try to interpret custom page sizes. If your application ignores the results returned by the driver, you risk incompatibility down the road.
Asynchronous LaserWriter driver no longer supported
Date Written: 9/24/91
Last reviewed: 8/1/92
What is causing incorrect characters to be printed on the asynchronous LaserWriter driver we licensed from Apple for use with a non-Apple PostScript-equipped printer?
___
The printing of incorrect characters is probably due to an incompatibility between the async driver and the version of PostScript being used in the printer.
The reason for the incompatibility is that the driver is no longer supported and hasn’t been revised for quite some time. The driver was originally developed outside of Apple, and Apple licensed the driver for its use. The company subsequently went out of business and the driver hasn’t been revved since.
PostScript and Apple’s AppleTalk LaserWriter drivers have been updated and changed quite a bit since then, which is why the characters print correctly when using AppleTalk, but not when printing asynchronously. The old async driver just doesn’t know how to handle the new PostScript versions and therefore prints “garbage” characters.
The SL Laser II driver is no longer supported by Apple, and at this time Apple has no plans to update this driver or write a new one. Since things work correctly with AppleTalk, you should confine usage to AppleTalk when printing PostScript to the LaserWriter. If this is unacceptable, you might check around with different clearinghouses to see if a third-party developer has a compatible, up-to-date asynchronous LaserWriter driver that you can use.
Use LaserWriter driver srcCopy instead of srcOr transfer mode
Date Written: 5/1/91
Last reviewed: 6/14/93
I’m creating PICTs that are comprised of many lines drawn in srcOr mode. When using the LaserWriter 6.x or 7.x driver with the Color/Grayscale radio button selected, some lines fail to print. Why is this happening?
___
The problem is a bug in the LaserWriter driver. Sometimes, when using a CGrafPort, the driver doesn’t reproduce lines drawn in srcOr mode. (A CGrafPort is used when the Color/Grayscale print option is selected; in Black & White print mode, a regular grafPort is used.) A workaround is to use srcCopy instead of srcOr when drawing QuickDraw objects within your PICTs.
Save and restore long word if using $948 under System 7
Date Written: 6/11/91
Last reviewed: 8/1/92
Unless my Macintosh application restores the contents of the long word at $948 after using that space for globals, the Finder draws icons incorrectly and my third-party LaserWriter driver crashes. Is somebody now using $948 for other purposes?
___
This is a known System 7 icon drawing bug, and also a bug with the printer driver that you are using.
The icon drawing utilities are trying to determine if a print page is currently open, by looking at that variable ($948 is part of printvars). This turns out not to be such a good idea, and it will be fixed in the next release of the system software.
The printer driver bug that you are experiencing is that every printer driver must return the variable at $948 to -1 when they are done printing. Also, while printing, the driver should set it to <> -1.
Update Backgrounder if using LaserWriter 7.0 under System 6
Date Written: 7/10/91
Last reviewed: 8/1/92
When we use the LaserWriter driver 7.0 with System 6.0, the Chooser’s Background Printing On button is always dimmed. Is there any way to enable background printing?
___
To get the LaserWriter 7.0 driver to work with System 6.0.x, update your Backgrounder file with the version on the Printing Tools disk that’s used to install System 7.0.
In short, to use the 7.0 LaserWriter Driver, 7.0 PrintMonitor, and System 6.0.x, place these files from the System 7.0 Printing Tools installation disks into your System Folder. All three must be used together for printing to work correctly. After you’ve placed these files in the System Folder, you should find the Background Printing option enabled for your use.
There are no known incompatibilities with the 7.0 drivers and System 6.0.x, so everyone should use the latest drivers, even with a mixed environment. This will also get rid of “printer wars” that occur when users use more than one version of the LaserWriter driver to print to the same printer. (The printer must be reinitialized if the current user uses a different version of the driver than that used by the previous user.)
LaserPrep 7.0 file & AppleShare Print Server
Date Written: 7/15/91
Last reviewed: 8/1/92
The LaserPrep 7.0 file is included on the System 7 Printing disk and the System 7 Group Update CD for upgrading the AppleShare Print Server to support the LaserWriter 7.0 driver. The AppleShare Print Server has its own LaserWriter driver built in and all it needs to print is a LaserPrep file. In fact, the AppleShare Print Server completely ignores any LaserWriter driver installed in the System Folder of the server.
Page 49 of the System 7 Group Upgrade Guide states the following procedure for upgrading an AppleShare Print Server to support the System 7 LaserWriter drivers:
1. Shut down the print server software.
2. Install the printer driver update…
3. Drag the LaserPrep icon from the Printer Update folder to the Server Folder of the print server Macintosh.
4. Restart the print server Macintosh and the AppleShare Print Server software.
Actually, step #2 in the System 7 Group Upgrade Guide is unnecessary. Installing the printer driver update has no effect on the AppleShare Print Server software.
LaserWriter 7.0 driver and LaserPrep dictionary
Date Written: 8/29/91
Last reviewed: 6/14/93
In the old LaserWriter drivers it was possible to create a PostScript file with or without the LaserPrep dictionary (“f” or “k” key). Is it possible to generate a file without the dictionary with the LaserWriter 7.0 driver?
___
In 7.0 printing, a LaserPrep dictionary is always sent with a print job. This is done to prevent constant reinitialization of the LaserWriter by conflicting printer drivers. You cannot prevent it from being sent. Fortunately the 7.0 version of the LaserPrep dictionary is much smaller (≈40K total) than its 6.x predecessors.
LaserWriter ignores ForeColor while filling smoothed polygon
Date Written: 10/8/91
Last reviewed: 8/1/92
When doing the FillRgn for drawing the fill of a smoothed polygon (as described in Macintosh Tech Note #91) the foreColor isn’t used on the LaserWriter. Any way to make it work?
___
You’ve discovered a design limitation of the LaserWriter driver. Things that have patterns associated with them are rendered by using the LaserWriter screen operators. This results in an assumption of the foreground color being black and the background color being white, which is what’s causing the problem you noticed. In short, it makes the driver ignore your foreground and background colors.
You can work around the problem by working in an off-screen GWorld first and then CopyBitsing everything to the printer port using srcCopy. There are a couple of problems with this approach: First, don’t do it with text or your text will be turned into a bitmap and you’ll get the “jaggies.” Second, you’ll probably want to increase the printer port’s resolution from its default of 72 dpi for better results. A value of 288 dpi works nicely since it’s an even multiple of QuickDraw’s native 72-dpi resolution. Also, make sure that the GWorld you create has the same bounds as the printer port’s rPage rectangle to avoid unnecessary scaling and clipping. After you’ve done all that, draw into it and CopyBits the result to the printer port. The nice thing about doing everything off-screen first is that then you can use some of the non-printer-friendly transfer modes like blend or dithered. Also, you can use ForeColor/BackColor and get the right thing this way.
If you don’t want to use this method for all printers, (since it can be quite a memory hog), you can check for the LaserWriter driver and use this method in just that case. For other drivers, you should just print as normal.
So, what do you need to do all this? Well, to set the resolution of the printer port, use PrGeneral as described in Inside Macintosh Volume V and develop #3.
To determine whether you have the LaserWriter driver or not, check the high byte of the wDev field in your print handle. This is described in the Tech Note “Optimizing For The LaserWriter—Techniques.” While this method might break some day, it’s currently the best way to determine which driver you’re using, and Apple will have to let developers know before we break it.
If the high byte of the wDev is 3, then you either have the LaserWriter driver or a third-party driver impersonating the LaserWriter. Some PostScript drivers do that because many apps assume a wDev of 3 means a PostScript printer, and anything else doesn’t. By acting like Apple’s LaserWriter driver, they get preferential treatment from apps that “special case” for PostScript. That’s not really a problem in this situation.
I discovered an interesting bug in the Macintosh LaserWriter driver. If the word “timeout” is in the name of a document, the LaserWriter driver will give a timeout error -8132. Are there similar magic words?
___
PostScript error messages are sent from the LaserWriter to the driver as text streams. The driver must check these strings to see if they contain an error message. If a document is named something that contains the same string as a PostScript error message, the driver may think there’s an error when the printer sends the “status: printing document XXXXX” message. Other strings cause similar problems; one of them is “printer out of paper.” If you want to see the rest of the strings, take a look at the LaserWriter printer driver resource type 'PREC' ID = 109.
Personal LaserWriter NTR has longer product string
Date Written: 3/17/92
Last reviewed: 6/14/93
The following line of my PostScript code causes the Personal LaserWriter NTR to gag, but it has worked with all other Apple LaserWriter printers up to now:
statusdict/product get str cvs show % Gets & prints the name of the printer
where str is defined as:
/str 20 string def
___
The product name stored for this printer is (LaserWriter Personal NTR) or (Personal LaserWriter NTR), depending on whether you’re using statusdict or systemdict, respectively. In either case, the product’s length is 24 characters. Since you’re only allocating 20 characters for the 2nd string you use with cvs, you’re getting a rangecheck error. Changing str to:
/str 24 string def
fixes the problem. You may want to make the string even larger than 24 characters to accommodate longer product names in the future.
Assumptions about the length of PostScript product strings are a common problem with new printers (having more verbose names). In fact, you should have the same trouble if you run your original PostScript on the Personal LaserWriter NT, which has a 23 character name.
LaserWriter II SC fonts and 4x bitmap size
Date Written: 11/17/89
Last reviewed: 6/14/93
Why do I need to have four times the bitmap size of the font I want to print in on the LaserWriter II SC?
___
LaserWriter II SC fonts are four times the point size of the associated Macintosh screen font. The pixels on the screen are four times as far apart (center to center) as the dots printed by the LaserWriter SC. When the bitmaps for one of the printer fonts is printed, the result is a font with a resolution four times greater than that of the font displayed, but at a size identical to the font displayed on the screen.
Font families shipped with LaserWriters
Date Written: 11/17/89
Last reviewed: 8/1/92
What fonts and sizes are shipped with the LaserWriter Plus, LaserWriter IInt, LaserWriter IIntx, and LaserWriter SC?
___
The LaserWriter Plus, LaserWriter IInt, and LaserWriter IIntx, shipped with a total of 11 font families with the sizes indicated below:
ITC Zapf Dingbats, New Century Schoolbook 10, 12, 14, 18, 24
A total of four font families are shipped with the SC: Courier, Symbol, Times, and Helvetica in the following sizes: 9, 10, 12, 14, 18, 24, 36, 48, 56, 72,
96.
Disable Graphics Smoothing for printing large bitmap images
Date Written: 5/3/89
Last reviewed: 6/14/93
When printing large (possibly scanned) Macintosh bitmap images, the page is printed with small lines running horizontally through the image. Why?
___
In System 6.0 the QuickDraw DrawPicture call was revised to fix some problems. One of these problems concerned large bitmaps. To help solve the problem of bitmaps that were too large to be printed, DrawPicture was modified to “band” the CopyBits request if it was too large. Banding is the process of converting a large bitmap into several smaller bitmaps. This banding usually occurs vertically down the page. When DrawPicture bands a large bitmap into pieces, the smoothing algorithm of the LaserWriter is applied to each piece separately, instead of being applied to the entire bitmap at once. Since the process of smoothing involves removing some pixels, hairlines will be created between the bitmap bands. There is no way to tell the LaserWriter driver that the smaller bitmaps are all part of one large bitmap, so the only solution to this problem is to disable Graphics Smoothing when printing large bitmap images.
Difference between LaserWriter 7.0 and 7.1 Namer
Date Written: 3/3/92
Last reviewed: 8/1/92
Apple Software Licensing’s “Exhibit C” lists localized Namer 7.0 versions in several languages, and mentions that these localized versions of the Namer are available in version 7.1. What is the difference in the Namer between 7.0 and 7.1?
___
The main difference between the LaserWriter utility 7.0 and 7.1 is that part of the Namer has been integrated into the 7.1 version of the software. Until the LaserWriter utility 7.1 was made available, the Namer was a stand-alone application. However, the LaserWriter utility does not do everything the Namer does. For example, the Namer renames AppleTalked ImageWriters, but the LaserWriter utility doesn’t. For all your printer configuration needs, you should use the 7.1 version of the software.
ImageWriter and printing multiple copies in draft mode
Date Written: 1/6/92
Last reviewed: 6/14/93
Our application can’t print multiple copies on the ImageWriter in Draft mode. If we type in 3 copies in the ImageWriter driver dialog box in Draft mode, we only get one copy. Everything prints fine in Best or Faster modes. Is this a bug in the ImageWriter driver?
___
Although it seems like this must be a bug in the ImageWriter driver, it’s not. In Draft mode, the application that’s printing must make sure the required number of pages are printed. This involves cycling through the app’s print loop for each copy to be printed.
In Draft mode on an ImageWriter, nothing is spooled to disk; the data is immediately sent to the printer. Therefore, the data is no longer available once it’s sent to the printer (and there’s no way for the print driver to resend it for multiple copies). When the ImageWriter spool-prints, the file that’s created is printed the required number of times for you. Therefore, your app only needs to handle multiple copies when printing in Draft mode.
Here’s what your app should do:
1. Validate the print record and present the job dialog. This allows the user to choose how many copies to print and allows the printer driver to adjust the print record to reflect how many copies your program has to print.
2. Get the number of copies that you’re expected to handle from the print record. In the case of spool printing, this number will be set to 1 (since the multiple copies are handled for you by the printer driver). In any case, the following Pascal code will give you the correct info.
numCopies:= printHdl^^.prJob.iCopies; (* From IM 2, pg. 151. *)
3. For each copy in numCopies, loop through the PrOpenDoc/PrCloseDoc section of your code.
This method is demonstrated in the code for the Macintosh Tech Note “A Printing Loop that Cares…,” which describes a model print loop.
Dogcow logo is trademarked
Date Written: 9/10/92
Last reviewed: 6/14/93
We would like to use the “dogcow” icon in our Page Setup dialog. Is the dogcow trademarked, and are there any restrictions on using this icon in our software?
___
Yes, the dogcow logo (along with its call, “Moof!”) is a trademark of Apple and is proprietary. The dogcow appears on Apple’s Developer CD Series disc and in other material. Apple has a pending U.S. registration on it. Accordingly, it’s not available to third-party developers as an icon or file symbol.
“Printer driver is MultiFinder compatible” bit
Date Written: 7/29/92
Last reviewed: 6/14/93
Could you tell me what the “printer driver is MultiFinder compatible” bit is used for?
___
The “printer driver is MultiFinder compatible” bit provides two features. First, it allows the printer driver resource file to be opened by multiple clients. This was obviously needed to support multiple applications printing simultaneously under MultiFinder. The other feature provided by the flag is the loading of PDEFs into the system heap rather than the application heap (which is where they go under the Finder).
The MultiFinder-compatible bit has a major limitation: If your driver has this flag set, you aren’t allowed to add or resize resources, or do anything else that would cause the RAM-resident resource map to change. Although MultiFinder lets multiple applications open the printer resource file at the same time, it has no control over the resource map that gets loaded by the Resource Manager when the file is opened. Because of this, each client gets its own personal copy of the resource map. When these clients get done with the file, they write the resource map back to the file (via UpdateResFile). Obviously, if the resources have changed in any way, the last client to call UpdateResFile is the only one whose changes will be recorded. This is a “thrill seeker” method of handling the printer driver resource files, but since none of the Apple printer drivers currently add or resize resources, it made sense.
So the bottom line here is that if you want your drivers to be compatible under MultiFinder, you’ll have to implement a scheme that doesn’t require adding or resizing resources. It’s OK to change the data in a resource, as long as you don’t change its size. Changing the data won’t cause changes to the resource map, so each client will still have accurate copies of the map.
Here’s what would happen to your printer driver’s resources under the Finder and MultiFinder when the MultiFinder-compatible bit is set:
• Under the Finder in system software version 6.0.x: All resources are loaded into the application heap—regardless of the resource attribute’s bit setting. If the resource has the “load into the system heap” bit set, it will still be loaded into the application heap under the Finder. This makes sense in the Finder world because the application heap will usually have more room than the system heap.
• Under MultiFinder in System 6 or System 7: All the printer driver’s resources will be loaded into the system heap. This is true whether the “load into the system heap” bit is set or not.
Why does the resource loading occur this way, even when the resource’s “load into the system heap” bit is set? Patches to the GetResource trap load all your printer driver’s resources into the system heap when the MultiFinder-compatible bit is set under MultiFinder, and into the application heap under the Finder (as described above), which is why you can’t override this behavior.
By the way, you should be aware of the SetPDiMC MPW tool, which is available on the Developer CD Series disc. It will automatically set the MultiFinder-compatible bit for you when you build your printer driver.
Use FillCRect off-screen, not directly to printer port
Date Written: 8/25/92
Last reviewed: 11/30/92
We’re having problems with color patterns using the LaserWriter driver version 7.1.2. If we create a 'ppat' resource in ResEdit (32 x 32 bits, in this case) and then do a FillCRect to the port returned by PrOpenDoc (with color set so that it’s a cGrafPort) with the pattern loaded by GetPixPat, we get a weird pattern. Doing the same to an off-screen GWorld and using CopyBits to copy to the printer port works fine, if a little slowly. Are we missing something here?
___
You need to use the FillCRect call off-screen rather than directly into the printer port, for at least two reasons. First, the LaserWriter driver doesn’t support filling objects with anything but black-and-white patterns because it uses the PostScript halftone screen functions to draw patterns. Second, the LaserWriter driver doesn’t understand (or handle) pixPats. Therefore, your only recourse is the one you discovered—to copy to and from GWorlds. Unfortunately, FillCRect doesn’t work with the LaserWriter drivers through version 7.2. After version 7.2 this probably won’t be a problem.
Color/Grayscale button on a non–Color QuickDraw Macintosh
Date Written: 8/31/92
Last reviewed: 6/14/93
The LaserWriter driver displays the Color/Grayscale option even on a non–Color QuickDraw Macintosh, which seems odd because the option normally causes the driver to return a CGrafPort to the calling application, and CGrafPorts are only available under Color QuickDraw. What happens if you’re printing original QuickDraw colors using the LaserWriter driver on a non–Color QuickDraw system? Does the driver just use the Black & White code regardless of whether the Color/Grayscale button is selected?
___
Regardless of whether Color/Grayscale is selected or not, the driver returns a grafPort because it’s not running on a Color QuickDraw machine. But, if the Color/Grayscale option is chosen on a Macintosh without Color QuickDraw, the driver doesn’t just use the Black & White code to print. Instead, it sends the colors to the printer as best it can without Color QuickDraw. This can result in three different sets of output when printing classic QuickDraw colors:
• Black & White button selected (identical output whether the system has Color QuickDraw or not)
• Color/Grayscale button selected on Color QuickDraw Macintosh
• Color/Grayscale button selected on non-Color QuickDraw Macintosh
PR 515 - Printing Manager Q&As
Printing
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As in this Technical Note:
OK to call WaitNextEvent if modal window is frontmost
OK to call WaitNextEvent if modal window is frontmost
Date Written: 1/20/93
Last reviewed: 6/14/93
I’ve read in “Print Hints: Top 10 Printing Crimes” that a program shouldn’t call WaitNextEvent while the Print Manager is open, but I call WaitNextEvent in my pIdleProc so my application can detect a user cancellation. Could this cause a problem?
___
The reason you shouldn’t call WaitNextEvent is to prevent the system from switching to another application. If you have a modal dialog-type window in front (the one with the modal window proc ID), the system will never switch applications and it’s safe to call WaitNextEvent. This is why all Apple’s drivers that call WaitNextEvent put up a modal dialog box. If you have a modal status window frontmost when your pIdle procedure is called, you’re in no danger.
Macintosh Printing Manager error -8132
Date Written: 1/1/90
Last reviewed: 6/14/93
What is Macintosh Printing Manager error -8132?
___
Error -8132 is generated when the selected printer times out. AppleTalk printers monitor the time between data packets that are sent to them. These devices will wait a maximum of two minutes without data until generating a time-out error. Applications such as database report generators sometimes require longer than two minutes to sort or format their data. These applications may receive the -8132 error. One workaround for this problem is to spool the report to a file that can be printed when sorting/formatting is completed.
Another possible solution is to change the amount of time the LaserWriter waits for data until it times out. Keep in mind that the time-out in the LaserWriter exists for a reason. Say a user starts printing a large document and leaves for the day. About halfway through the document, the user’s Macintosh crashes, leaving the printer waiting without data. In two minutes, the LaserWriter times out, and is once again available for other users on the net. However if the time-out is disabled by the first user’s job, the printer is useless until rebooted. To avoid disabling the time-out, increase the time-out value so that the device times-out if the job takes too long to print, but does not time-out for normal delays. This is done by sending the following Postscript to the LaserWriter with a Postscript downloading utility:
serverdict begin 0 exitserver
statusdict begin 0 0 0 setdefaulttimeouts
See the Apple’s LaserWriter Reference for more information.
The only other way to handle this problem is to periodically send some data to the printer. Do this about once a minute or so to prevent the printer from timing out. Use the PostScriptHandle PicComment to send this “tickle” data so that it is only sent to PostScript printers that have the ability to time-out. (Quickdraw printers won’t see the data.) Another reason for using the PostScriptHandle PicComment involves the internal buffering of the LaserWriter driver. The LaserWriter driver maintains a 4K internal buffer for the PostScript that it sends. If you send just a few characters as your tickle data, they are buffered in the driver, and the device still times-out. The only way to ensure that your tickle data makes it to the device is to send a 4K block via the PostScriptHandle PicComment. Needless to say, sending 4K blocks over AppleTalk once a minute is going to cause some network traffic, but it’s still another possible workaround.
Macintosh Printing Manager error -8133
Date Written: 1/1/90
Last reviewed: 8/1/92
What is Macintosh Printing Manager error -8133?
___
Printing Manager error -8133 occurs when the PostScript interpreter of the LaserWriter (or any other PostScript printer) generates a PostScript error. A description of the PostScript command that caused the error is displayed in the status window. This error often occurs when an application sends PostScript directly to the printer, and that PostScript contains an error. To debug this problem, look at the PostScript generated by the driver. Hold down the Command-F key right after clicking okay in the Print dialog. A file named PostScript0 will be created in the current directory. With the 7.x LaserWriter drivers, just select the PostScript File radio button in the Print dialog.
Identifying Macintosh’s currently selected printer
Date Written: 1/1/90
Last reviewed: 8/1/92
How can I find out which printer the user has selected with the Macintosh Chooser? How can I change it without going through the Chooser?
___
The type of the currently selected printer is stored in the System file in 'STR ' resource ID -8192. This will be the name of the driver selected, such as LaserWriter or ImageWriter. The name of the actual printer selected will be in the driver file (same name as the printer type chosen) in 'PAPA' resource ID -8192. Look at this resource with ResEdit to see what is in it.
You can change the 'STR ' and 'PAPA' resources, but it may not work now or in the future. DTS does not recommend changing the system file or circumventing the Chooser! The Chooser is a very complicated piece of software, and it has many dependencies. Implementing the chooser within your application would make your application dependent on a particular version of the system software. Like the Chooser, your application would then have to be revised each time a new system is released.
How to save Macintosh Page Setup and Print dialog options
Date Written: 5/3/89
Last reviewed: 6/14/93
How do I save options set in the Macintosh Page Setup and Print dialogs?
___
The only supported method to save the dialog options is to save the Print Record that was passed to the PrStlDialog/PrJobDialog calls. This print record contains the options chosen in the Page Setup and Print dialogs. Although these options are saved in the print record, they cannot be used as defaults for the Print dialog. For example, say the user chooses landscape printing in the Page Setup dialog, and sets the number of copies to 5 in the Print dialog. If the print record is passed to PrStlDialog, the dialog shows that landscape printing was selected in the print record. However, if that print record is passed to PrJobDialog, number of copies is shown as 1, because options in the PrJobDialog are considered to be “job dependent,” as opposed to “document dependent.”
The best method for saving the print record is to save it as a resource in your document’s resource fork. Since the print record is already pointed to by a valid handle, creating a resource is easy. Here are the required steps:
1. Save the refNum returned by CurResFile;
2. Make sure the resource fork of the document file is opened using OpenResFile (IM I:115).
3. Now that you have a place to put the resource, you must get the resource manager to create it for you. First, pick a type and ID for your resource. You can use the Unique ID function (IM I:121) to generate the ID. Next, call the AddResource procedure (IM I:124). This procedure takes four parameters:
Parameter Type What to Pass
--------------------------
theData Handle The handle to the currently valid print record.
theType ResType The four character resource type you have chosen.
theID INTEGER The ID returned from the UniqueID function.
name Str255 The name for this resource or '' if none.
4. Call WriteResource (IM I:125), and UpdateResFile (IM I:125) to make sure the new resource gets saved into the file.
5. Reset the currently selected file back to whatever it was before you began the save using UseResFile (IM I:117).
The important things to keep in mind when doing this are:
1. Make your resource type something different than those used by the Print Manager. This prevents the Print Manager from getting confused and grabbing the wrong resource. Types to avoid include 'PREC', 'PDEF', and 'POST'.
2. Don’t make any assumptions about the size of the print record. If you really need to know the size, use GetHandleSize.so that if the record gets bigger in the future, your code will still work.
3. Be sure to pass the record that you get from your document to PrValidate before using it. This ensures that the record gets updated if any changes have been made to record structure or contents since the record was saved.
MacApp and SetRsl print driver resolution
Date Written: 4/16/92
Last reviewed: 6/14/93
We’ve been trying to use the standardPrint handler from MacApp 2 to print some views. The problem is that we would like the printout to come out at 300 dpi on the LaserWriter, and the print handler’s device resolution fields (which come from the device driver) tell it to draw at 72 dpi. How, can I get a PenSize(1,1); MoveTo(100,100); LineTo(300,300); draw at 300 dpi and make a nice fine line? Please refer me to the needed documentation.
___
The mechanism by which your application can find out the chosen printer’s real resolution, and then draw with it, is documented “Meet PrGeneral, the Trap That Makes the Most of the Printing Manager,” in issue #3 of develop magazine (July 1990). You can find it, along with its accompanying code and application, on the Developer CD, E.T.O., and AppleLink.
The DTS Sample Code SC.009.FracApp300 on the Developer CD shows how to use PrGeneral in an application built with MacApp 2.
Printing patterns at higher resolutions with GetRslData & SetRsl
Date Written: 11/7/90
Last reviewed: 6/14/93
Is there a way that I can make my Macintosh fill patterns print at a higher resolution (more than 72 dpi), but have my patterns still print as patterns?
___
To make your patterns print at the printer’s resolution, you need to use Printing Manager PrGeneral’s GetRslData and SetRsl opcodes to get and set the resolution, and you must scale the pattern to match. Let me explain.
If we do not scale our patterns up to the printer’s resolution before print time, we would get “big chunky” patterns because the printer driver would need to scale the patterns on the fly from 72 dpi to its resolution. Therefore, we use the “cookie cutter” approach to “place” the pattern into the object that is being filled. The size of the cookie cutter (that is, the destination Rect) depends on the “scaleFactor”. For example, a scaleFactor of 2 will have a destination rect of 16 x 16. We will then CopyBits the pattern one square at a time into the object that is being filled.
XRefs:
Inside Macintosh Volume V, pages 410-416
“Meet PrGeneral,” develop, July 1990
Call Printing Mgr’s bottleneck to send StdText to printer driver
Date Written: 11/7/90
Last reviewed: 6/14/93
We need to use the Macintosh toolbox call StdText instead of DrawChar, DrawString, and DrawText to accomplish horizontal scaling and fractional point sizes, but StdText does not work on the LaserWriter IISC. Is there any workaround?
___
The problem is that you are calling the low-level bottleneck directly, and the Printing Manager has replaced the bottleneck completely with its own. So when you are actually printing, the print driver will not see your call to StdText. The work-around for this is to call the Print Manager’s bottleneck itself when you are printing. You can do this in the following way:
/* typedef the function */
typedef pascal void (*StdTextProcPtr)( short, Ptr, Point, Point );
void tstDrawStuff( pInfo, thePort, pageNum )
TPrint pInfo;
GrafPtr thePort;
unsigned short pageNum;
{
StdTextProcPtr Proc; /* declare a variable here */
/* do your clipping etc. */
/* TEST -- no good w/o PriMon */
/* do your moveto etc. */
/* Is there a replacement bottleneck ?*/
if ( thePort->grafProcs ) {
Proc = (StdTextProcPtr)thePort->grafProcs->textProc; /* yes, so grab it */
This technique will work in both background and foreground printing. I have tested this solution on the Personal LaserWriter SC and the LaserWriter IINTX, and it prints fine.
Modifying PDEFs to have printer driver jump into dialog code
Date Written: 11/9/90
Last reviewed: 6/14/93
Could you please point me to the correct section in “Learning to Drive” (or send me an example) that we would need to modify the appropriate PDEF resource in order to have the driver jump to our dialog code?
___
Pages 15-18 discuss the contents of the various PDEFs. You will need patch the appropriate PDEF to get your required functionality. You should also realize that patching PDEFs is not an easy task, nor will it always survive “new” Macintosh LaserWriter driver releases. I want you to know that before you go too far down the PDEF patching road.
By the way, we do know of a couple of developers that have patched PDEFs successfully. The following paragraphs are a “high-level” look at patching a PDEF:
To patch a PDEF, you must write an installer application that lets users choose the printer driver they want to modify via Standard File. You can cause Standard File to display only printer drivers by telling it to display only files of type “PRER”. Once the user has selected a driver you need to scan the driver for the PDEF 0 and 1 resources. When you find one of these you read it in and modify it.
However, I'll give you a quick overview of what you need to do. PDEF 0 & 1 both start with jump tables (a table of JMP instructions followed by the PC relative address to jump to). The JMP to PrClosePage always begins at offset 12. So what your installer needs to do after it loads the resource is grab the eighth word in the resource. That is the offset to the actual start of the PrClosePage code. Then you have to resize the handle so that it is big enough to hold your patch and then BlockMove {IM II-44} your code into the end of the handle. The following illustration might help:
JMP $0486 << Note: some PDEF's have multiple JMPs at
* - - - - - - - - - - - - - * the beginning of the code. The
* * original offset to PrClosePage was
* >> Original PDEF code << * $0386.
* *
$0386 --> PrClosePage << Note: most PDEF's have multiple printing
* * calls defined.
* *
* - - - - - - - - - - - - - *
$0486 --> {{{ Your Code }}}
* * << You resize the resource handle to this
* * and blockmove your code in here.
* *
* - - - - - - - - - - - - - *
JMP $0386 << At this point we jump into the printer
driver's original code for PrClosePage.
Note: The previous illustration is not to scale nor completely accurate in regards to the addresses used. Please consult “Learning To drive” for the correct offsets for each PDEF.
Then replace the original offset to PrClosePage with the offset to your new code. Remember that this is PC relative so you would calculate it like this: If the original PrClosePage JMP was JMP 10(SP) and the size of the original PDEF was 50 bytes, then you need to replace the JMP 10(SP) with JMP 50-16(SP). In other words, you need to JMP to what was formerly the end of the PDEF resource but what is now the beginning of your code (subtract 16 because the offset table is 16 bytes long. Refer to “Learning to Drive” for more details). Then on the end of your code, add a JMP ((SizeOfJumpTable + originalOffset) – (sizeofOriginalPDEF + SizeOfYourCode))(SP). Your code will end with a JMP to a negative offset off the PC back to the original PrCloseRoutine.
If you are willing to support only system software later than version 3.3 (basically that means that you won’t be supporting Macintosh 512K and 512KE computers), you don’t have to deal with all the PDEF changing, and you can simply patch out the $A8FD trap and look for the proper selector on the stack. (Use SysEnvirons to be sure that you are on a system later than version 3.3) This will work because the newer libraries (that is, any library that shipped since the Macintosh II shipped) first checks for the trap and jumps there if the trap exists so even if the application has linked in the printing library they still end up calling the trap on systems after 3.3.
Determining if printer supports color and/or grayscale printing
Date Written: 3/14/91
Last reviewed: 8/1/92
Is there any way to determine whether I’m printing to either a color printer or a printer simulating color, such as the LaserWriter set for Color/Grayscale?
___
Check the grafPort returned by your call to PrOpenDoc. If the rowBytes of the grafPort is less than 0, the Printing Manager has returned a color grafPort. You can then make Color QuickDraw calls into this grafPort. LaserWriter driver version 6.0 and later returns a color grafPort from the PrOpenDoc call, if the Color/Grayscale button has been set.
The following code fragment demonstrates this:
(* This function determines whether the port passed to it is a *)
(* color port. If so, it returns TRUE. *)
FUNCTION IsColorPort(portInQuestion: GrafPtr): BOOLEAN;
BEGIN
IF portInQuestion^.portBits.rowBytes < 0 THEN
IsColorPort := TRUE
ELSE
IsColorPort := FALSE;
END;
A third-party printer driver should return the type of grafPort that represents the abilities of its printer. Therefore, if the printer supports color and/or grayscale, and if Color QuickDraw is present, the application will receive a color grafPort after calling PrOpenDoc. Otherwise, if the Macintosh you’re running on does not support Color QuickDraw, you should return a black-and-white grafPort.
Using LaserWriter fonts with StyleWriter
Date Written: 3/22/91
Last reviewed: 6/14/93
Can StyleWriter use all the Adobe fonts for my LaserWriter? Can StyleWriter print the encapsulated PostScript drawings I’ve developed? Does TrueType software come with the StyleWriter?
___
The difference between the StyleWriter and a regular LaserWriter (other than one’s got a laser and the other doesn’t) is that the LaserWriter has PostScript built-in; the StyleWriter does not. The StyleWriter, in fact, has nothing built-in, and only recognizes basic “put this dot here” types of commands. Even sending it pure ASCII accomplishes nothing.
TrueType software is shipped with the StyleWriter, because it can image fonts at any resolution with excellent quality, much like PostScript. The TrueType software is intended for users of system versions 6.0.7 and later, but prior to System 7. System 7 includes support for TrueType.
You can use PostScript images and fonts with the StyleWriter, but just as with the ImageWriter, you’ll have to have something that images them in memory before sending them to the printer (in other words, a Macintosh-resident PostScript interpreter).
Personal LaserWriter LS 1.1 driver is faster
Date Written: 8/9/91
Last reviewed: 6/14/93
My LaserWriter LS is very slow when printing from my Macintosh SE. Any suggestions?
___
Use the new LaserWriter LS driver version 1.1 or later. It was released after System 7.0, and is available on AppleLink or from your dealer.
If you’re printing Word files, Microsoft’s TrueType INIT will help. It’s available from MicroSoft Technical Support or from commercial bulletin board services.
ImageWriter II 6.1 & 7.0 driver PrGeneral/PrStlDialog bug
Date Written: 9/11/91
Last reviewed: 6/14/93
If an application uses PrGeneral to set draftBits mode on an ImageWriter II (using the 6.1 or 7.0 drivers) and then does a PrStlDialog, a fatal crash occurs. This happens with both the AppleTalk and non-AppleTalk drivers. This only happens on Macintosh systems with Color QuickDraw.
The problem is connected to the two 'dctb' resources which were added in the 6.1 driver. When the landscape icon is being grayed out (since it’s unsupported in draftBits mode), the driver is jamming the gray pen pattern directly into the GrafPort. While this brute-force method works fine with GrafPorts, the 'dctb' resources make the Dialog Manager use a CGrafPort now. Jamming the pattern into this CGrafPort as if it were a GrafPort makes a mess and causes a fatal crash, manifesting itself as a “division by zero” error, for example.
To work around the problem, check to see if you are printing to a 6.1 or 7.0 ImageWriter driver, and if so, do the style dialog before setting draftBits. The disadvantage of this is that setting draftBits before calling PrStlDialog causes the 50% reduction and landscape controls to be disabled, and this method doesn’t. Therefore, after the style dialog, you should check to see if the user selected landscape printing or 50% reduction by calling PrGeneral (getRotnOp) and checking bit 3 of the wDev field (See “Learning To Drive” on the Developer CDs for more about the wDev.) If the user selected either of those options, display a dialog warning them that those features will be ignored. Then, call PrGeneral to set draftBits and continue normally. PrGeneral will adjust the print record so that options not supported with draftBits are turned off. If a non 6.1/7.0 ImageWriter driver is used, the code should set draftBits as usual, before the PrStlDialog call.
This workaround is the better alternative to hacking the 'dctb's out of the driver or patching around the problem, although it doesn’t help 7.0 users running applications that already make use of the draftBits option. The following Pascal snippet demonstrates how to use the workaround:
(* Sample snippet that demonstrates how to work-around the
'dctb' bug in the ImageWriter II 6.1 and 7.0 drivers.
Bug causes a crash when doing a style dialog
after setting draftbits with PrGeneral.
*)
{
What's what in the code snippet that follows.
thePrRecHdl : THPrint;
rslData : TSetRslBlk;
draftData : TDftBitsBlk;
rotnData : TGetRotnBlk;
isBadImageWriter : Boolean;
isLandscape : Boolean;
isReduced : Boolean;
}
PrOpen;
IF (PrError = noErr) THEN
BEGIN
PrintDefault(thePrRecHdl);
IF (PrError = noErr) THEN
BEGIN
{See if we're using a version 6.1 or 7.0 ImageWriter driver.}
IF (thePrRecHdl^^.prStl.wDev DIV 256) = 1 THEN
isBadImageWriter := (PrDrvrVers = 61) OR (PrDrvrVers = 70)
ELSE
isBadImageWriter := FALSE;
IF NOT (isBadImageWriter) THEN
BEGIN
{Set draftBits mode now, if not our special case.}
draftData.iOpCode := draftBitsOp;
draftData.hPrint := thePrRecHdl;
PrGeneral(@draftData);
END;
{Do the style dialog.}
IF (PrStlDialog(thePrRecHdl)) THEN
IF (isBadImageWriter) THEN
BEGIN
{If so, see if they selected rotation or 50% reduction.}
rotnData.iOpCode := getRotnOp;
rotnData.hPrint := thePrRecHdl;
PrGeneral(@rotnData);
isLandscape := rotnData.fLandscape;
isReduced := BTst(thePrRecHdl^^.prStl.wDev, 3);
{If so, warn them that those things will be ignored.
{Now, set draftBits mode, since we didn't before.}
draftData.iOpCode := draftBitsOp;
draftData.hPrint := thePrRecHdl;
PrGeneral(@draftData);
END;
IF (PrJobDialog(thePrRecHdl)) THEN
BEGIN
{Print}
END;
END;
END;
PrClose;
How to abort printing while PrPicFile is executing
Date Written: 9/27/91
Last reviewed: 6/14/93
Can I abort printing from within PrPicFile? My attempts haven’t been succesful.
___
The following Pascal procedure is roughly the same as the default idle procedure—the one the system supplies for you when you don’t supply one of your own:
procedure MyPIdleProcedure;
var
pIdleEventRecord: EventRecord;
begin
if GetNextEvent(keyDownMask, pIdleEventRecord) then
begin
if ((BitAnd(pIdleEventRecord.message, charCodeMask) =
(This is written in Think Pascal, but it should work fine under MPW Pascal if you change the “BitAnd” functions to “BAND”.)
As you can see, the system aborts when you press Command-period by using PrSetError to the constant iPrAbort, which is the same error returned when the user clicks “Cancel” in the style or job dialogs. If you set the error to iPrAbort, the system will take care of the rest. Your print loop will also gracefully cancel, provided you follow the guidelines in the Macintosh Technical Note “A Printing Loop That Cares.”
Disabling ImageWriter’s initial formfeed
Date Written: 1/23/92
Last reviewed: 6/14/93
When our product-to-be prints to the ImageWriter II there is an automatic (and undesirable in our product) sheet feed before actual printing begins. To make matters worse, it always feeds 11", even though our form is a different length. Is there a way to avoid this?
___
The ImageWriter ejects a page before printing with the “No Gaps” option selected because it assumes that the page is aligned with a small gap between the perforation and the print head. So the printer ejects the page and re-aligns the print head at the perforation of the next page.
The sample code below illustrates how to prevent that nasty form feed from occurring. The program temporarily disables the “No Gaps” option if it is selected until after PrOpenDoc is called, preventing a formfeed from occurring. After PrOpenDoc is called, the program re-enables the “No Gaps” option.
Please note that this sample only works with the ImageWriter II and the ImageWriter LQ printers—probably the only series that supports the “No Gaps” option. The sample shows how to check for these types of printers, so you shouldn’t have any problems.
{ Draw whatever you want here. Make sure it fits in the world rectangle. }
BEGIN
MoveTo(100, 100);
DrawString('testing...');
END;
FUNCTION HiByte(word: INTEGER): Byte;
BEGIN
HiByte := word DIV 256;
END;
PROCEDURE PrintStuff;
VAR
thePrRec: THPrint;
thePrPort: TPPrPort;
theStatus: TPrStatus;
oldPort: GrafPtr;
theError: OSErr;
theVers: INTEGER;
NoGaps: BOOLEAN;
BEGIN
GetPort(oldPort);
thePrRec := THPrint(NewHandle(SIZEOF(TPrint)));
PrOpen;
IF PrError = noErr THEN BEGIN
PrintDefault(thePrRec);
IF NOT PrStlDialog(thePrRec) THEN
PrSetError(iPrAbort);
IF NOT PrJobDialog(thePrRec) THEN
PrSetError(iPrAbort);
(* First, lock the handle down so we can do a cool with statement. *)
HLock(Handle(thePrRec));
IF PrError = noErr THEN
WITH thePrRec^^ DO BEGIN
(* Okay, now get device dependent. We only want to do this if we *)
(* know the selected printer driver supports it. *)
IF (HiByte(prStl.wDev) = bDevImageWriter) OR
(HiByte(prStl.wDev) = bDevImageWriterLQ) THEN BEGIN
(* Now remember the state of the nogaps option. *)
NoGaps := BitTst(@prStl.wDev, 11);
(* If NoGaps was selected, then turn it off until we PrOpenDoc.*)
IF (NoGaps) THEN BitClr(@prStl.wDev, 11);
(* If we're on the LQ driver, we need to copy the wDev into the*)
(* PrintX array, or the LQ driver won't notice that it changed.*)
IF (HiByte(prStl.wDev) = bDevImageWriterLQ) THEN
PrintX[2] := prStl.wDev;
END;
END;
HUnlock(Handle(thePrRec));
(* Now open the document. *)
thePrPort := PrOpenDoc(thePrRec, NIL, NIL);
(* Now we need to see if the NoGaps option was selected. If it was *)
(* then we turned it off before calling PrOpenDoc, so we need to *)
(* turn it back on. If wasn't selected, we won't do anything. *)
HLock(Handle(thePrRec));
WITH thePrRec^^ DO BEGIN
IF NoGaps AND
(HiByte(prStl.wDev) = bDevImageWriter) OR
(HiByte(prStl.wDev) = bDevImageWriterLQ) THEN BEGIN
(* Turn it on for the ImageWriter. *)
BitSet(@prStl.wDev, 11);
(* Copy it over for the ImageWriter LQ. *)
IF (HiByte(prStl.wDev) = bDevImageWriterLQ) THEN
PrintX[2] := prStl.wDev;
END;
END;
HUnlock(Handle(thePrRec));
IF PrError = noErr THEN BEGIN
PrOpenPage(thePrPort, NIL);
IF PrError = noErr THEN BEGIN
DrawStuff(thePrRec^^.prInfo.rPage);
END;
PrClosePage(thePrPort);
END;
PrCloseDoc(thePrPort);
IF (thePrRec^^.prJob.bJDocLoop = bSpoolLoop) and (PrError = noErr)
THEN PrPicFile(thePrRec, NIL, NIL, NIL, theStatus);
END;
PrClose;
DisposHandle(Handle(thePrRec));
SetPort(oldPort);
END;
BEGIN
InitGraf(@thePort); {initialize QuickDraw}
InitFonts; {initialize Font Manager}
FlushEvents(everyEvent, 0); {call OS Event Mgr to discard}
{any previous events}
InitWindows; {initialize Window Manager}
InitMenus; {initialize Menu Manager}
TEInit; {initialize TextEdit}
InitDialogs(NIL); {initialize Dialog Manager}
InitCursor; {call QuickDraw to make cursor (pointer) an arrow}
PrintStuff;
END.
Update Macintosh color table if printing in color
Date Written: 5/3/89
Last reviewed: 6/14/93
I’m having trouble printing in color. When I copy a color image from a Macintosh offscreen pixmap to the printer, I get a black piece of paper. If I draw directly it comes out fine. What am I doing wrong?
___
When you built your offscreen port and copied the color table from a GDevice, did you change the color table to reflect that the color table is now part of a pixmap? In a GDevice the color table’s value field is zero for all entries, but in a pixmap the value field represents the index value of each color in the table. If the color table has not been converted, printing won’t work properly.
PR 520 - PrintMonitor Q&As
Printing
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As in this Technical Note:
Determining whether background printing is enabled
Determining whether background printing is enabled
62755 MD JM dy 1
Date Written: 1/20/93
Last reviewed: 4/26/93
How can I tell whether background printing is enabled for the selected printer?
___
There’s no procedure to determine whether the user has picked background printing because that’s not a standard Macintosh system software feature. Where it’s done, it’s implemented on a driver-by-driver basis, and each printer driver keeps track of it in a different way. Apple does it one way, Hewlett-Packard might do it another way, GCC a third way, and so on.
Since we don’t actually know how it’s done, and since Apple doesn’t guarantee its drivers will do it the same way in the future as they do now, we have no information to give you on how to determine this. Sorry to be the bearer of bad news.
Location for spool files under System 7.0
Date Written: 12/18/90
Last reviewed: 8/1/92
Is there a recommended location for printer driver writers to put their own spool files?
___
Place it in the “PrintMonitor Documents” folder within a “personal spool” folder. PrintMonitor will ignore other folders and it will delete its spool file when it was done with it.
The biggest advantage with this approach is that all the spool files end up in the same location, which is probably the best solution for the native user.
There are a few cases when the folder might not be present (such as if the user has deleted it for some reason). Therefore, you should check to make sure that the folder is present before trying to add a file to it. If it is not present, be sure to create it with the exact name; otherwise, PrintMonitor will not be able to find it and will create a second folder with a different name, possibly confusing the native user. Folder management is relatively straightforward with the addition of the “Folder Manager” to System 7.0. The Finder chapter in Inside Macintosh Volume VI contains the details about using it.
Determining whether background printing is turned on
Date Written: 2/5/92
Last reviewed: 8/1/92
I need to know whether the currently chosen printer driver—that I’m about to print to—is going to be doing background printing or not. The user interface of our application is somewhat different, depending on this. Currently, I check the driver’s file type to see whether it’s network- or local-attached, generalizing “network = LaserWriter = background capable” and “direct = ImageWriter = foreground only,” but I know this isn’t always correct, because a variety of direct-connect printer drivers now support background printing. What I really need is a way to check specifically for background printing being turned on. Is there a safe and driver-independent way to check this?
___
No, there’s absolutely no safe and driver-independent way to do this. Although Apple uses PrintMonitor for all background printing, it’s not designed for third-party products to hook into it. The “background printing” internals aren’t documented because changing that outside an individual printer driver may not give the driver all the information it needs to correctly print in the background. Also, third parties have to implement their own background printing schemes, and there’s absolutely no way to know how they’re storing the choice on whether or not to print in the background.
If your user interface is substantially different, you may need to require your users to enable background printing in your application as well as in the Chooser. This isn’t exactly friendly, but since there’s no mechanism for applications to communicate with printer drivers to find out about background printing, it’s pretty much your only choice.
You’re right, checking a driver’s file type to see whether it’s network- or local-attached is not a good solution. StyleWriters and LaserWriter LS printers print in the background but don’t use a network. Some third-party laser printers support AppleTalk but not background printing.
Turning Macintosh background printing on and off
Date Written: 3/25/92
Last reviewed: 6/14/93
Someone has requested that we do an extension for our application to turn background printing on and off. Can you tell me how to do this?
___
Unfortunately, DTS can’t tell you how to turn background printing on and off, for the following reasons:
1. Apple’s mechanism for background printing (PrintMonitor) is at this time only available to Apple’s printer drivers. How Apple’s printer drivers store the background printing choice is private because Engineering has changed it in the past and might need to change it again.
2. Since all third-party printer drivers that do background printing have to do it their own way, they all have to find their own ways of recording the “background printing” choice, such as storing the information in resources like the PAPA resource. Since there are no guidelines for recording background printing options, DTS cannot recommend a method which will work with all drivers.
For these reasons, and to avoid possible Human Interface conflicts, background printing control is best left to the user.
Macintosh PicComments and background printing
Date Written: 5/3/89
Last reviewed: 6/14/93
My Macintosh application prints fine when Background Printing is disabled, but doesn’t print or prints blank pages when Background Printing is enabled. Why?
___
The Printing Manager gets execution time by replacing the grafProcs of the Printing Manager GrafPort (returned from the PrOpenDoc call). This GrafPort is created by the PrOpenDoc call, and is reinitialized by every call to PrOpenPage. On the LaserWriter driver with Background Printing enabled, this reinitialization causes the clipping region of the port to be set to (0,0,0,0). On the next call to StdLine or StdText, for example, the Printing Manager notices the zeroed clipping region, and sets it to the rectangle specified by prInfo.rPage in the print record. If your application prints its documents with PostScript using PicComments, the Printing Manager never gets a chance to define the clipping region. All of your PostScript code is sent to the LaserWriter and executed, but the results are clipped to (0,0,0,0). To work around this problem, you must call at least one Quickdraw drawing routine at the beginning of each page. The following is frequently used:
PrOpenPage(...);
MoveTo(-32000, -32000);
Line(0, 0);
{ PostScript code here }
PrClosePage(...);
The MoveTo is needed so that the line is drawn outside of the imageable area of the page, and thus is invisible. The Line call calls the Printing Manager’s version of the StdLine procedure, which defines the clipping region for the port. All subsequent PostScript code will be clipped correctly. Since PrOpenPage resets the clipping region, the MoveTo/Line combination must be done after each call to PrOpenPage.
SetOrigin and background printing
Date Written: 1/1/90
Last reviewed: 6/14/93
I have noticed PostScript graphics printing in different locations depending on whether or not Background printing is enabled on the Macintosh. What causes this?
___
In LaserWriter 4.0 - 5.2, SetOrigin is handled differently when Background printing is enabled. The method used by these drivers is described below:
Background Printing DISABLED: The application calls SetOrigin, and QuickDraw responds to the call by adjusting the portRect of the Print Manager’s GrafPort. Since SetOrigin does not cause any grafProcs to run, the Printing Manager doesn’t see this change until the next QuickDraw drawing operation such as DrawString or LineTo. At that time, the Printing Manager notices the change in the portRect, and updates its internal origin. All QuickDraw graphics are now localized to the new origin. All PostScript graphics will also get the localization to the new origin.
Background Printing ENABLED: Because QuickDraw is playing back a picture that was spooled earlier, it handles calls to SetOrigin a little differently. When the SetOrigin call is encountered in a DrawPicture call, the portRect of the GrafPort is NOT updated. Instead, QuickDraw keeps the current origin cached, and offsets each graphic on the fly. Since the portRect was not modified, the Printing Manager does not see the SetOrigin call. Although all QuickDraw objects are still localized correctly (by QuickDraw), PostScript graphics do not move to the new origin. Instead, the application localizes the coordinates of its PostScript graphics. You could also use the technique documented in the Technical Note “Position-Independent PostScript.”
The implementation of the SetOrigin (IM I:166) and ClipRect (IM I:167) calls has been revised in LaserWriter 6.0. This is a general description of those changes:
LaserWriter 6.0: Both QuickDraw and PostScript coordinates are offset with respect to the origin set by SetOrigin. The driver now inserts a picture comment into the spool file so that the Print Monitor knows that the origin was changed during spooling.
Since applications must support versions of the LaserWriter driver other than 6.0, they should still use the method described in this Tech Note to make sure their PostScript is position-independent.
X-Ref:
DTS Macintosh Technical Note “Position-Independent PostScript”
Macintosh spool file formats aren’t available
Date Written: 1/1/90
Last reviewed: 8/1/92
What is file format of Macintosh Printing Manager and LaserShare spool files?
___
Unfortunately, the formats for these files are not currently available, and it is doubtful that they will be made available in the future. Whenever new features are added to LaserShare, the Printing Manager, or other parts of the Macintosh toolbox, it’s not uncommon for these spool file formats to be affected. If the file format were published, and essentially frozen, it would be much harder for engineering to add features.
PR 525 - QuickDraw GX Printing Q&As
Printing
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As for this month:
QuickDraw GX printing extensions and creator types
QuickDraw GX printing extensions and creator types
Date Written: 5/3/93
Last reviewed: 7/2/93
A QuickDraw GX printing extension I’ve already written works fine, but when I install my latest creation, it doesn’t show up in the print dialog—only my old printing extension does. Both extensions are the same except for a few lines of code in their gxDespoolPage message overrides. What’s going on?
___
Your printing extensions shouldn’t have the same creator type. QuickDraw GX requires unique creator types for drivers and printing extensions, just as the Finder does for applications. A creator type must be unique because QuickDraw GX uses it to build its chain of message handlers. If two printing extensions have the same creator, there’s no way to determine which is which in the chain. You can register creator types for your printer drivers and printing extensions with the Developer Support Center (AppleLink DEVSUPPORT).
NewMessageGlobals and global data
Date Written: 2/1/93
Last reviewed: 7/2/93
Why can’t I get NewMessageGlobals to work in my gxInitialize message for my QuickDraw GX printing extension? The global data I try to initialize isn’t being accessed correctly.
___
You shouldn’t call NewMessageGlobals from any routine in which you access your global data. Otherwise, because of optimization that your compiler may perform, the data references can be invalid. Instead, use an approach like this:
extern long A5Size(void);
extern void A5Init(void *);
typedef struct GlobalType {
StringHandle aString;
} GlobalType;
GlobalType myGlobals;
/* This routine sets the initial values of our global data. */
OSErr InitGlobalData()
{
OSErr err;
// Initialize our globals.
myGlobals.aString = GetString(r_myStringID);
err = ResError();
if (!err) DetachResource(myGlobals.aString);
return err;
}
/* Our override for the gxInitialize message. */
OSErr MyInitialize()
{
OSErr err;
// Create an A5 world, then go initialize our global data.
err = NewMessageGlobals(A5Size(), A5Init());
if (!err) err = InitGlobalData();
return err;
}
A detailed explanation of the problem accompanies the Kabooms printing extension sample on the Developer CD.
When a QuickDraw GX PostScript printer driver generates a file, will it work for Level 1 and Level 2 printers?
___
The LaserWriter driver bundled with QuickDraw GX produces a flavor of PostScript that we call “portable.” This flavor is meant to work on the widest range of printing devices, be they Level 1 or 2, color or black and white.
QuickDraw GX PostScript Level 2 features used
Date Written: 11/17/92
Last reviewed: 7/2/93
What PostScript Level 2 features does the QuickDraw GX printing mechanism take advantage of?
___
The Level 2 features used in QuickDraw GX mostly have to do with patterns, text, and bitmaps:
• QuickDraw GX patterns are converted into Level 2 pattern dictionaries when going to a Level 2 printer. The actual PostScript code emitted by the driver differs little with respect to patterns when going to Level 1 or Level 2. However, the procedures defined in the header do something entirely different on a Level 2 printer.
• Line layout in QuickDraw GX takes advantage of the xshow, yshow, and xyshow operators when it makes sense to do so.
• The Level 2 rectangle operators are used in Level 2, though this happens in the procedures defined in the header rather than in PostScript code from the driver.
• On Level 2 printers, the indexed color spaces are used for printing bitmaps up to eight bits deep.
• The plan is to use Level 2 device-independent color when possible.
QuickDraw GX printer drivers and color option
Date Written: 9/14/92
Last reviewed: 7/2/93
The LaserWriter driver before the QuickDraw GX version has an option to print in Color/Grayscale or Black & White. Why isn’t this option in the QuickDraw GX LaserWriter driver?
___
The Color/Grayscale option was added to the LaserWriter driver only for compatibility reasons. At the time, some applications couldn’t deal with the color option (specifically with cGrafPorts), so a Black & White mode was also provided. The Black & White mode exhibited the same functionality as the earlier LaserWriter driver 5.2.
Because most applications are color compatible now, the option was removed from the QuickDraw GX printer drivers. Some people reported that the option let them print faster when they chose Black & White. This was true because of quirks in the earlier LaserWriter driver version. Under QuickDraw GX, this shouldn’t be a problem. If it turns out to be a problem for your driver, you could incorporate black-and-white threshold printing into your “rough draft mode” code. The QuickDraw GX LaserWriter driver always prints in color.